Merge "Controls UI - Loading animations" into rvc-dev
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index c43fabd..68c27a8 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -21,18 +21,12 @@
     path: "java",
 }
 
-java_sdk_library {
+java_library {
     name: "framework-permission",
-    defaults: ["framework-module-defaults"],
     srcs: [
         ":framework-permission-sources",
     ],
-
-    // TODO(b/155480189) - Remove naming_scheme once references have been resolved.
-    // Temporary java_sdk_library component naming scheme to use to ease the transition from separate
-    // modules to java_sdk_library.
-    naming_scheme: "framework-modules",
-
+    sdk_version: "module_current",
     apex_available: [
         "com.android.permission",
         "test_com.android.permission",
@@ -46,5 +40,91 @@
     visibility: [
         "//frameworks/base/apex/permission:__subpackages__",
     ],
-    stubs_library_visibility: ["//visibility:public"],
+}
+
+stubs_defaults {
+    name: "framework-permission-stubs-defaults",
+    srcs: [ ":framework-permission-sources" ],
+    libs: [ "framework-annotations-lib" ],
+    dist: { dest: "framework-permission.txt" },
+}
+
+droidstubs {
+    name: "framework-permission-stubs-srcs-publicapi",
+    defaults: [
+        "framework-module-stubs-defaults-publicapi",
+        "framework-permission-stubs-defaults",
+    ],
+    check_api: {
+        last_released: {
+            api_file: ":framework-permission.api.public.latest",
+            removed_api_file: ":framework-permission-removed.api.public.latest",
+        },
+        api_lint: {
+            new_since: ":framework-permission.api.public.latest",
+        },
+    },
+}
+
+droidstubs {
+    name: "framework-permission-stubs-srcs-systemapi",
+    defaults: [
+        "framework-module-stubs-defaults-systemapi",
+        "framework-permission-stubs-defaults",
+    ],
+    check_api: {
+        last_released: {
+            api_file: ":framework-permission.api.system.latest",
+            removed_api_file: ":framework-permission-removed.api.system.latest",
+        },
+        api_lint: {
+            new_since: ":framework-permission.api.system.latest",
+        },
+    },
+}
+
+droidstubs {
+    name: "framework-permission-api-module_libs_api",
+    defaults: [
+        "framework-module-api-defaults-module_libs_api",
+        "framework-permission-stubs-defaults",
+    ],
+    check_api: {
+        last_released: {
+            api_file: ":framework-permission.api.module-lib.latest",
+            removed_api_file: ":framework-permission-removed.api.module-lib.latest",
+        },
+        api_lint: {
+            new_since: ":framework-permission.api.module-lib.latest",
+        },
+    },
+}
+
+droidstubs {
+    name: "framework-permission-stubs-srcs-module_libs_api",
+    defaults: [
+        "framework-module-stubs-defaults-module_libs_api",
+        "framework-permission-stubs-defaults",
+    ],
+}
+
+java_library {
+    name: "framework-permission-stubs-publicapi",
+    srcs: [ ":framework-permission-stubs-srcs-publicapi" ],
+    defaults: ["framework-module-stubs-lib-defaults-publicapi"],
+    dist: { dest: "framework-permission.jar" },
+}
+
+java_library {
+    name: "framework-permission-stubs-systemapi",
+    srcs: [ ":framework-permission-stubs-srcs-systemapi" ],
+    defaults: ["framework-module-stubs-lib-defaults-systemapi"],
+    dist: { dest: "framework-permission.jar" },
+}
+
+java_library {
+    name: "framework-permission-stubs-module_libs_api",
+    srcs: [ ":framework-permission-stubs-srcs-module_libs_api" ],
+    defaults: ["framework-module-stubs-lib-defaults-module_libs_api"],
+    dist: { dest: "framework-permission.jar" },
 }
diff --git a/core/java/com/android/internal/BrightnessSynchronizer.java b/core/java/com/android/internal/BrightnessSynchronizer.java
index aa23251..42724be 100644
--- a/core/java/com/android/internal/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/BrightnessSynchronizer.java
@@ -84,17 +84,17 @@
      * Converts between the int brightness system and the float brightness system.
      */
     public static float brightnessIntToFloat(Context context, int brightnessInt) {
-        PowerManager pm = context.getSystemService(PowerManager.class);
-        float pmMinBrightness = pm.getBrightnessConstraint(
+        final PowerManager pm = context.getSystemService(PowerManager.class);
+        final float pmMinBrightness = pm.getBrightnessConstraint(
                 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
-        float pmMaxBrightness = pm.getBrightnessConstraint(
+        final float pmMaxBrightness = pm.getBrightnessConstraint(
                 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
-        int minBrightnessInt = brightnessFloatToInt(pmMinBrightness, PowerManager.BRIGHTNESS_MIN,
-                PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
-                PowerManager.BRIGHTNESS_ON);
-        int maxBrightnessInt = brightnessFloatToInt(pmMaxBrightness, PowerManager.BRIGHTNESS_MIN,
-                PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
-                PowerManager.BRIGHTNESS_ON);
+        final int minBrightnessInt = Math.round(brightnessFloatToIntRange(pmMinBrightness,
+                PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX,
+                PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON));
+        final int maxBrightnessInt = Math.round(brightnessFloatToIntRange(pmMaxBrightness,
+                PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX,
+                PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON));
 
         return brightnessIntToFloat(brightnessInt, minBrightnessInt, maxBrightnessInt,
                 pmMinBrightness, pmMaxBrightness);
@@ -119,34 +119,43 @@
      * Converts between the float brightness system and the int brightness system.
      */
     public static int brightnessFloatToInt(Context context, float brightnessFloat) {
-        PowerManager pm = context.getSystemService(PowerManager.class);
-        float pmMinBrightness = pm.getBrightnessConstraint(
-                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
-        float pmMaxBrightness = pm.getBrightnessConstraint(
-                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
-        int minBrightnessInt = brightnessFloatToInt(pmMinBrightness, PowerManager.BRIGHTNESS_MIN,
-                PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
-                PowerManager.BRIGHTNESS_ON);
-        int maxBrightnessInt = brightnessFloatToInt(pmMaxBrightness, PowerManager.BRIGHTNESS_MIN,
-                PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_OFF + 1,
-                PowerManager.BRIGHTNESS_ON);
-
-        return brightnessFloatToInt(brightnessFloat, pmMinBrightness, pmMaxBrightness,
-                minBrightnessInt, maxBrightnessInt);
+        return Math.round(brightnessFloatToIntRange(context, brightnessFloat));
     }
 
     /**
-     * Converts between the float brightness system and the int brightness system.
+     * Converts between the float brightness system and the int brightness system, but returns
+     * the converted value as a float within the int-system's range. This method helps with
+     * conversions from one system to the other without losing the floating-point precision.
      */
-    public static int brightnessFloatToInt(float brightnessFloat, float minFloat, float maxFloat,
-            int minInt, int maxInt) {
+    public static float brightnessFloatToIntRange(Context context, float brightnessFloat) {
+        final PowerManager pm = context.getSystemService(PowerManager.class);
+        final float minFloat = pm.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
+        final float maxFloat = pm.getBrightnessConstraint(
+                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
+        final float minInt = brightnessFloatToIntRange(minFloat,
+                PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX,
+                PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON);
+        final float maxInt = brightnessFloatToIntRange(maxFloat,
+                PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX,
+                PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON);
+        return brightnessFloatToIntRange(brightnessFloat, minFloat, maxFloat, minInt, maxInt);
+    }
+
+    /**
+     * Translates specified value from the float brightness system to the int brightness system,
+     * given the min/max of each range.  Accounts for special values such as OFF and invalid values.
+     * Value returned as a float privimite (to preserve precision), but is a value within the
+     * int-system range.
+     */
+    private static float brightnessFloatToIntRange(float brightnessFloat, float minFloat,
+            float maxFloat, float minInt, float maxInt) {
         if (floatEquals(brightnessFloat, PowerManager.BRIGHTNESS_OFF_FLOAT)) {
             return PowerManager.BRIGHTNESS_OFF;
         } else if (Float.isNaN(brightnessFloat)) {
             return PowerManager.BRIGHTNESS_INVALID;
         } else {
-            return Math.round(MathUtils.constrainedMap((float) minInt, (float) maxInt, minFloat,
-                    maxFloat, brightnessFloat));
+            return MathUtils.constrainedMap(minInt, maxInt, minFloat, maxFloat, brightnessFloat);
         }
     }
 
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index cff669e..9950e55 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -3506,13 +3506,11 @@
         }
 
         /**
-         * Only expand direct share area if there is a minimum number of shortcuts,
-         * which will help reduce the amount of visible shuffling due to older-style
-         * direct share targets.
+         * Only expand direct share area if there is a minimum number of targets.
          */
         private boolean canExpandDirectShare() {
             int orientation = getResources().getConfiguration().orientation;
-            return mChooserListAdapter.getNumShortcutResults() > getMaxTargetsPerRow()
+            return mChooserListAdapter.getNumServiceTargetsForExpand() > getMaxTargetsPerRow()
                     && orientation == Configuration.ORIENTATION_PORTRAIT
                     && !isInMultiWindowMode();
         }
@@ -3721,8 +3719,12 @@
 
                 // only expand if we have more than maxTargetsPerRow, and delay that decision
                 // until they start to scroll
-                if (mChooserMultiProfilePagerAdapter.getActiveListAdapter()
-                        .getSelectableServiceTargetCount() <= maxTargetsPerRow) {
+                ChooserListAdapter adapter =
+                        mChooserMultiProfilePagerAdapter.getActiveListAdapter();
+                int validTargets =
+                        mAppendDirectShareEnabled ? adapter.getNumServiceTargetsForExpand()
+                                : adapter.getSelectableServiceTargetCount();
+                if (validTargets <= maxTargetsPerRow) {
                     mHideDirectShareExpansion = true;
                     return;
                 }
diff --git a/core/java/com/android/internal/app/ChooserListAdapter.java b/core/java/com/android/internal/app/ChooserListAdapter.java
index 492f98c..2568d09 100644
--- a/core/java/com/android/internal/app/ChooserListAdapter.java
+++ b/core/java/com/android/internal/app/ChooserListAdapter.java
@@ -79,6 +79,7 @@
 
     private static final int MAX_SUGGESTED_APP_TARGETS = 4;
     private static final int MAX_CHOOSER_TARGETS_PER_APP = 2;
+    private static final int MAX_SERVICE_TARGET_APP = 8;
 
     static final int MAX_SERVICE_TARGETS = 8;
 
@@ -98,6 +99,7 @@
     private ChooserTargetInfo
             mPlaceHolderTargetInfo = new ChooserActivity.PlaceHolderTargetInfo();
     private int mValidServiceTargetsNum = 0;
+    private int mAvailableServiceTargetsNum = 0;
     private final Map<ComponentName, Pair<List<ChooserTargetInfo>, Integer>>
             mParkingDirectShareTargets = new HashMap<>();
     private final Map<ComponentName, Map<String, Integer>> mChooserTargetScores = new HashMap<>();
@@ -603,7 +605,13 @@
             Pair<List<ChooserTargetInfo>, Integer> parkingTargetInfoPair =
                     mParkingDirectShareTargets.getOrDefault(origComponentName,
                             new Pair<>(new ArrayList<>(), 0));
-            parkingTargetInfoPair.first.addAll(parkingTargetInfos);
+            for (ChooserTargetInfo target : parkingTargetInfos) {
+                if (!checkDuplicateTarget(target, parkingTargetInfoPair.first)
+                        && !checkDuplicateTarget(target, mServiceTargets)) {
+                    parkingTargetInfoPair.first.add(target);
+                    mAvailableServiceTargetsNum++;
+                }
+            }
             mParkingDirectShareTargets.put(origComponentName, parkingTargetInfoPair);
             rankTargetsWithinComponent(origComponentName);
             if (isShortcutResult) {
@@ -648,7 +656,7 @@
             List<ChooserTargetInfo> parkingTargets = parkingTargetsItem.first;
             int insertedNum = parkingTargetsItem.second;
             while (insertedNum < quota && !parkingTargets.isEmpty()) {
-                if (!checkDuplicateTarget(parkingTargets.get(0))) {
+                if (!checkDuplicateTarget(parkingTargets.get(0), mServiceTargets)) {
                     mServiceTargets.add(mValidServiceTargetsNum, parkingTargets.get(0));
                     mValidServiceTargetsNum++;
                     insertedNum++;
@@ -663,9 +671,6 @@
                         + " totalScore=" + totalScore
                         + " quota=" + quota);
             }
-            if (mShortcutComponents.contains(component)) {
-                mNumShortcutResults += insertedNum - parkingTargetsItem.second;
-            }
             mParkingDirectShareTargets.put(component, new Pair<>(parkingTargets, insertedNum));
         }
         if (!shouldWaitPendingService) {
@@ -681,19 +686,15 @@
             return;
         }
         Log.i(TAG, " fillAllServiceTargets");
-        int maxRankedTargets = mChooserListCommunicator.getMaxRankedTargets();
-        List<ComponentName> topComponentNames = getTopComponentNames(maxRankedTargets);
+        List<ComponentName> topComponentNames = getTopComponentNames(MAX_SERVICE_TARGET_APP);
         // Append all remaining targets of top recommended components into direct share row.
         for (ComponentName component : topComponentNames) {
             if (!mParkingDirectShareTargets.containsKey(component)) {
                 continue;
             }
             mParkingDirectShareTargets.get(component).first.stream()
-                    .filter(target -> !checkDuplicateTarget(target))
+                    .filter(target -> !checkDuplicateTarget(target, mServiceTargets))
                     .forEach(target -> {
-                        if (mShortcutComponents.contains(component)) {
-                            mNumShortcutResults++;
-                        }
                         mServiceTargets.add(mValidServiceTargetsNum, target);
                         mValidServiceTargetsNum++;
                     });
@@ -706,28 +707,34 @@
                 .map(pair -> pair.first)
                 .forEach(targets -> {
                     for (ChooserTargetInfo target : targets) {
-                        if (!checkDuplicateTarget(target)) {
+                        if (!checkDuplicateTarget(target, mServiceTargets)) {
                             mServiceTargets.add(mValidServiceTargetsNum, target);
                             mValidServiceTargetsNum++;
-                            mNumShortcutResults++;
                         }
                     }
                 });
         mParkingDirectShareTargets.clear();
     }
 
-    private boolean checkDuplicateTarget(ChooserTargetInfo chooserTargetInfo) {
+    private boolean checkDuplicateTarget(ChooserTargetInfo target,
+            List<ChooserTargetInfo> destination) {
         // Check for duplicates and abort if found
-        for (ChooserTargetInfo otherTargetInfo : mServiceTargets) {
-            if (chooserTargetInfo.isSimilar(otherTargetInfo)) {
+        for (ChooserTargetInfo otherTargetInfo : destination) {
+            if (target.isSimilar(otherTargetInfo)) {
                 return true;
             }
         }
         return false;
     }
 
-    int getNumShortcutResults() {
-        return mNumShortcutResults;
+    /**
+     * The return number have to exceed a minimum limit to make direct share area expandable. When
+     * append direct share targets is enabled, return count of all available targets parking in the
+     * memory; otherwise, it is shortcuts count which will help reduce the amount of visible
+     * shuffling due to older-style direct share targets.
+     */
+    int getNumServiceTargetsForExpand() {
+        return mAppendDirectShareEnabled ? mAvailableServiceTargetsNum : mNumShortcutResults;
     }
 
     /**
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e670f1f..4510b87 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -685,6 +685,7 @@
         </activity>
 
         <activity android:name=".controls.management.ControlsEditingActivity"
+                  android:label="@string/controls_menu_edit"
                   android:theme="@style/Theme.ControlsManagement"
                   android:excludeFromRecents="true"
                   android:noHistory="true"
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 76ca385..09918e7 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -169,5 +169,8 @@
     <item type="id" name="screen_recording_options" />
     <item type="id" name="screen_recording_dialog_source_text" />
     <item type="id" name="screen_recording_dialog_source_description" />
+
+    <item type="id" name="accessibility_action_controls_move_before" />
+    <item type="id" name="accessibility_action_controls_move_after" />
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8c10f61..8bbcfa0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2711,6 +2711,8 @@
     <string name="accessibility_control_change_favorite">favorite</string>
     <!-- a11y action to unfavorite a control. It will read as "Double-tap to unfavorite" in screen readers [CHAR LIMIT=NONE] -->
     <string name="accessibility_control_change_unfavorite">unfavorite</string>
+    <!-- a11y action to move a control to the position specified by the parameter [CHAR LIMIT=NONE] -->
+    <string name="accessibility_control_move">Move to position <xliff:g id="number" example="1">%d</xliff:g></string>
 
     <!-- Controls management controls screen default title [CHAR LIMIT=30] -->
     <string name="controls_favorite_default_title">Controls</string>
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
index 175ed06..00a406e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
@@ -48,6 +48,8 @@
 
     private var modified = false
 
+    override val moveHelper = null
+
     override val favorites: List<ControlInfo>
         get() = favoriteIds.mapNotNull { id ->
             val control = controls.firstOrNull { it.control.controlId == id }?.control
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index 4b283d6..2f91710 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName
 import android.graphics.Rect
+import android.os.Bundle
 import android.service.controls.Control
 import android.service.controls.DeviceTypes
 import android.view.LayoutInflater
@@ -78,7 +79,7 @@
                         background = parent.context.getDrawable(
                                 R.drawable.control_background_ripple)
                     },
-                    model is FavoritesModel // Indicates that position information is needed
+                    model?.moveHelper // Indicates that position information is needed
                 ) { id, favorite ->
                     model?.changeFavoriteStatus(id, favorite)
                 }
@@ -176,12 +177,14 @@
 
 /**
  * Holder for using with [ControlStatusWrapper] to display names of zones.
+ * @param moveHelper a helper interface to facilitate a11y rearranging. Null indicates no
+ *                   rearranging
  * @param favoriteCallback this callback will be called whenever the favorite state of the
  *                         [Control] this view represents changes.
  */
 internal class ControlHolder(
     view: View,
-    val withPosition: Boolean,
+    val moveHelper: ControlsModel.MoveHelper?,
     val favoriteCallback: ModelFavoriteChanger
 ) : Holder(view) {
     private val favoriteStateDescription =
@@ -197,7 +200,11 @@
         visibility = View.VISIBLE
     }
 
-    private val accessibilityDelegate = ControlHolderAccessibilityDelegate(this::stateDescription)
+    private val accessibilityDelegate = ControlHolderAccessibilityDelegate(
+        this::stateDescription,
+        this::getLayoutPosition,
+        moveHelper
+    )
 
     init {
         ViewCompat.setAccessibilityDelegate(itemView, accessibilityDelegate)
@@ -207,7 +214,7 @@
     private fun stateDescription(favorite: Boolean): CharSequence? {
         if (!favorite) {
             return notFavoriteStateDescription
-        } else if (!withPosition) {
+        } else if (moveHelper == null) {
             return favoriteStateDescription
         } else {
             val position = layoutPosition + 1
@@ -256,15 +263,67 @@
     }
 }
 
+/**
+ * Accessibility delegate for [ControlHolder].
+ *
+ * Provides the following functionality:
+ * * Sets the state description indicating whether the controls is Favorited or Unfavorited
+ * * Adds the position to the state description if necessary.
+ * * Adds context action for moving (rearranging) a control.
+ *
+ * @param stateRetriever function to determine the state description based on the favorite state
+ * @param positionRetriever function to obtain the position of this control. It only has to be
+ *                          correct in controls that are currently favorites (and therefore can
+ *                          be moved).
+ * @param moveHelper helper interface to determine if a control can be moved and actually move it.
+ */
 private class ControlHolderAccessibilityDelegate(
-    val stateRetriever: (Boolean) -> CharSequence?
+    val stateRetriever: (Boolean) -> CharSequence?,
+    val positionRetriever: () -> Int,
+    val moveHelper: ControlsModel.MoveHelper?
 ) : AccessibilityDelegateCompat() {
 
     var isFavorite = false
 
+    companion object {
+        private val MOVE_BEFORE_ID = R.id.accessibility_action_controls_move_before
+        private val MOVE_AFTER_ID = R.id.accessibility_action_controls_move_after
+    }
+
     override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
         super.onInitializeAccessibilityNodeInfo(host, info)
 
+        info.isContextClickable = false
+        addClickAction(host, info)
+        maybeAddMoveBeforeAction(host, info)
+        maybeAddMoveAfterAction(host, info)
+
+        // Determine the stateDescription based on the holder information
+        info.stateDescription = stateRetriever(isFavorite)
+        // Remove the information at the end indicating row and column.
+        info.setCollectionItemInfo(null)
+
+        info.className = Switch::class.java.name
+    }
+
+    override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean {
+        if (super.performAccessibilityAction(host, action, args)) {
+            return true
+        }
+        return when (action) {
+            MOVE_BEFORE_ID -> {
+                moveHelper?.moveBefore(positionRetriever())
+                true
+            }
+            MOVE_AFTER_ID -> {
+                moveHelper?.moveAfter(positionRetriever())
+                true
+            }
+            else -> false
+        }
+    }
+
+    private fun addClickAction(host: View, info: AccessibilityNodeInfoCompat) {
         // Change the text for the double-tap action
         val clickActionString = if (isFavorite) {
             host.context.getString(R.string.accessibility_control_change_unfavorite)
@@ -276,13 +335,30 @@
             // “favorite/unfavorite”
             clickActionString)
         info.addAction(click)
+    }
 
-        // Determine the stateDescription based on the holder information
-        info.stateDescription = stateRetriever(isFavorite)
-        // Remove the information at the end indicating row and column.
-        info.setCollectionItemInfo(null)
+    private fun maybeAddMoveBeforeAction(host: View, info: AccessibilityNodeInfoCompat) {
+        if (moveHelper?.canMoveBefore(positionRetriever()) ?: false) {
+            val newPosition = positionRetriever() + 1 - 1
+            val moveBefore = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                MOVE_BEFORE_ID,
+                host.context.getString(R.string.accessibility_control_move, newPosition)
+            )
+            info.addAction(moveBefore)
+            info.isContextClickable = true
+        }
+    }
 
-        info.className = Switch::class.java.name
+    private fun maybeAddMoveAfterAction(host: View, info: AccessibilityNodeInfoCompat) {
+        if (moveHelper?.canMoveAfter(positionRetriever()) ?: false) {
+            val newPosition = positionRetriever() + 1 + 1
+            val moveAfter = AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+                MOVE_AFTER_ID,
+                host.context.getString(R.string.accessibility_control_move, newPosition)
+            )
+            info.addAction(moveAfter)
+            info.isContextClickable = true
+        }
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
index 37b6d15..2543953 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
@@ -42,6 +42,8 @@
      */
     val elements: List<ElementWrapper>
 
+    val moveHelper: MoveHelper?
+
     /**
      * Change the favorite status of a particular control.
      */
@@ -69,6 +71,34 @@
          */
         fun onFirstChange()
     }
+
+    /**
+     * Interface to facilitate moving controls from an [AccessibilityDelegate].
+     *
+     * All positions should be 0 based.
+     */
+    interface MoveHelper {
+
+        /**
+         * Whether the control in `position` can be moved to the position before it.
+         */
+        fun canMoveBefore(position: Int): Boolean
+
+        /**
+         * Whether the control in `position` can be moved to the position after it.
+         */
+        fun canMoveAfter(position: Int): Boolean
+
+        /**
+         * Move the control in `position` to the position before it.
+         */
+        fun moveBefore(position: Int)
+
+        /**
+         * Move the control in `position` to the position after it.
+         */
+        fun moveAfter(position: Int)
+    }
 }
 
 /**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt
index 411170cb..5242501 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.controls.management
 
 import android.content.ComponentName
+import android.util.Log
 import androidx.recyclerview.widget.ItemTouchHelper
 import androidx.recyclerview.widget.RecyclerView
 import com.android.systemui.controls.ControlInterface
@@ -39,9 +40,39 @@
     private val favoritesModelCallback: FavoritesModelCallback
 ) : ControlsModel {
 
+    companion object {
+        private const val TAG = "FavoritesModel"
+    }
+
     private var adapter: RecyclerView.Adapter<*>? = null
     private var modified = false
 
+    override val moveHelper = object : ControlsModel.MoveHelper {
+        override fun canMoveBefore(position: Int): Boolean {
+            return position > 0 && position < dividerPosition
+        }
+
+        override fun canMoveAfter(position: Int): Boolean {
+            return position >= 0 && position < dividerPosition - 1
+        }
+
+        override fun moveBefore(position: Int) {
+            if (!canMoveBefore(position)) {
+                Log.w(TAG, "Cannot move position $position before")
+            } else {
+                onMoveItem(position, position - 1)
+            }
+        }
+
+        override fun moveAfter(position: Int) {
+            if (!canMoveAfter(position)) {
+                Log.w(TAG, "Cannot move position $position after")
+            } else {
+                onMoveItem(position, position + 1)
+            }
+        }
+    }
+
     override fun attachAdapter(adapter: RecyclerView.Adapter<*>) {
         this.adapter = adapter
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index cf098d5..960c501 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -156,7 +156,7 @@
                 sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
 
                 // Remove notification
-                notificationManager.cancel(NOTIFICATION_RECORDING_ID);
+                notificationManager.cancel(NOTIFICATION_VIEW_ID);
 
                 startActivity(Intent.createChooser(shareIntent, shareLabel)
                         .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
@@ -175,7 +175,7 @@
                         Toast.LENGTH_LONG).show();
 
                 // Remove notification
-                notificationManager.cancel(NOTIFICATION_RECORDING_ID);
+                notificationManager.cancel(NOTIFICATION_VIEW_ID);
                 Log.d(TAG, "Deleted recording " + uri);
                 break;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 9cfb1b2..839ea69 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -56,6 +56,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
+import android.provider.Settings;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.MathUtils;
@@ -160,6 +161,9 @@
     static final String EXTRA_CANCEL_NOTIFICATION = "android:screenshot_cancel_notification";
     static final String EXTRA_DISALLOW_ENTER_PIP = "android:screenshot_disallow_enter_pip";
 
+    // From WizardManagerHelper.java
+    private static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete";
+
     private static final String TAG = "GlobalScreenshot";
 
     private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
@@ -460,6 +464,13 @@
             return;
         }
 
+        if (!isUserSetupComplete()) {
+            // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
+            // and sharing shouldn't be exposed to the user.
+            saveScreenshotAndToast(finisher);
+            return;
+        }
+
         // Optimizations
         mScreenBitmap.setHasAlpha(false);
         mScreenBitmap.prepareToDraw();
@@ -546,6 +557,41 @@
     }
 
     /**
+     * Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
+     * failure).
+     */
+    private void saveScreenshotAndToast(Consumer<Uri> finisher) {
+        // Play the shutter sound to notify that we've taken a screenshot
+        mScreenshotHandler.post(() -> {
+            mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
+        });
+
+        saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
+            @Override
+            void onActionsReady(SavedImageData imageData) {
+                finisher.accept(imageData.uri);
+                if (imageData.uri == null) {
+                    mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_NOT_SAVED);
+                    mNotificationsController.notifyScreenshotError(
+                            R.string.screenshot_failed_to_capture_text);
+                } else {
+                    mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SAVED);
+
+                    mScreenshotHandler.post(() -> {
+                        Toast.makeText(mContext, R.string.screenshot_saved_title,
+                                Toast.LENGTH_SHORT).show();
+                    });
+                }
+            }
+        });
+    }
+
+    private boolean isUserSetupComplete() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
+    }
+
+    /**
      * Clears current screenshot
      */
     private void dismissScreenshot(String reason, boolean immediate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
new file mode 100644
index 0000000..7f7ff9cf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import android.app.PendingIntent
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.NotifInteractionLog
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import javax.inject.Inject
+
+/**
+ * Logger class for events related to the user clicking on notification actions
+ */
+class ActionClickLogger @Inject constructor(
+    @NotifInteractionLog private val buffer: LogBuffer
+) {
+    fun logInitialClick(
+        entry: NotificationEntry?,
+        pendingIntent: PendingIntent
+    ) {
+        buffer.log(TAG, LogLevel.DEBUG, {
+            str1 = entry?.key
+            str2 = entry?.ranking?.channel?.id
+            str3 = pendingIntent.intent.toString()
+        }, {
+            "ACTION CLICK $str1 (channel=$str2) for pending intent $str3"
+        })
+    }
+
+    fun logRemoteInputWasHandled(
+        entry: NotificationEntry?
+    ) {
+        buffer.log(TAG, LogLevel.DEBUG, {
+            str1 = entry?.key
+        }, {
+            "  [Action click] Triggered remote input (for $str1))"
+        })
+    }
+
+    fun logStartingIntentWithDefaultHandler(
+        entry: NotificationEntry?,
+        pendingIntent: PendingIntent
+    ) {
+        buffer.log(TAG, LogLevel.DEBUG, {
+            str1 = entry?.key
+            str2 = pendingIntent.intent.toString()
+        }, {
+            "  [Action click] Launching intent $str2 via default handler (for $str1)"
+        })
+    }
+
+    fun logWaitingToCloseKeyguard(
+        pendingIntent: PendingIntent
+    ) {
+        buffer.log(TAG, LogLevel.DEBUG, {
+            str1 = pendingIntent.intent.toString()
+        }, {
+            "  [Action click] Intent $str1 launches an activity, dismissing keyguard first..."
+        })
+    }
+
+    fun logKeyguardGone(
+        pendingIntent: PendingIntent
+    ) {
+        buffer.log(TAG, LogLevel.DEBUG, {
+            str1 = pendingIntent.intent.toString()
+        }, {
+            "  [Action click] Keyguard dismissed, calling default handler for intent $str1"
+        })
+    }
+}
+
+private const val TAG = "ActionClickLogger"
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index bf28040..9181c69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -54,7 +54,7 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.dagger.StatusBarModule;
+import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -114,6 +114,7 @@
     private final SmartReplyController mSmartReplyController;
     private final NotificationEntryManager mEntryManager;
     private final Handler mMainHandler;
+    private final ActionClickLogger mLogger;
 
     private final Lazy<StatusBar> mStatusBarLazy;
 
@@ -138,14 +139,18 @@
             mStatusBarLazy.get().wakeUpIfDozing(SystemClock.uptimeMillis(), view,
                     "NOTIFICATION_CLICK");
 
+            final NotificationEntry entry = getNotificationForParent(view.getParent());
+            mLogger.logInitialClick(entry, pendingIntent);
+
             if (handleRemoteInput(view, pendingIntent)) {
+                mLogger.logRemoteInputWasHandled(entry);
                 return true;
             }
 
             if (DEBUG) {
                 Log.v(TAG, "Notification click handler invoked for intent: " + pendingIntent);
             }
-            logActionClick(view, pendingIntent);
+            logActionClick(view, entry, pendingIntent);
             // The intent we are sending is for the application, which
             // won't have permission to immediately start an activity after
             // the user switches to home.  We know it is safe to do at this
@@ -158,11 +163,15 @@
                 Pair<Intent, ActivityOptions> options = response.getLaunchOptions(view);
                 options.second.setLaunchWindowingMode(
                         WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
+                mLogger.logStartingIntentWithDefaultHandler(entry, pendingIntent);
                 return RemoteViews.startPendingIntent(view, pendingIntent, options);
             });
         }
 
-        private void logActionClick(View view, PendingIntent actionIntent) {
+        private void logActionClick(
+                View view,
+                NotificationEntry entry,
+                PendingIntent actionIntent) {
             Integer actionIndex = (Integer)
                     view.getTag(com.android.internal.R.id.notification_action_index_tag);
             if (actionIndex == null) {
@@ -170,7 +179,7 @@
                 return;
             }
             ViewParent parent = view.getParent();
-            StatusBarNotification statusBarNotification = getNotificationForParent(parent);
+            StatusBarNotification statusBarNotification = entry.getSbn();
             if (statusBarNotification == null) {
                 Log.w(TAG, "Couldn't determine notification for click.");
                 return;
@@ -212,10 +221,10 @@
             }
         }
 
-        private StatusBarNotification getNotificationForParent(ViewParent parent) {
+        private NotificationEntry getNotificationForParent(ViewParent parent) {
             while (parent != null) {
                 if (parent instanceof ExpandableNotificationRow) {
-                    return ((ExpandableNotificationRow) parent).getEntry().getSbn();
+                    return ((ExpandableNotificationRow) parent).getEntry();
                 }
                 parent = parent.getParent();
             }
@@ -255,7 +264,7 @@
     };
 
     /**
-     * Injected constructor. See {@link StatusBarModule}.
+     * Injected constructor. See {@link StatusBarDependenciesModule}.
      */
     public NotificationRemoteInputManager(
             Context context,
@@ -265,13 +274,15 @@
             Lazy<StatusBar> statusBarLazy,
             StatusBarStateController statusBarStateController,
             @Main Handler mainHandler,
-            RemoteInputUriController remoteInputUriController) {
+            RemoteInputUriController remoteInputUriController,
+            ActionClickLogger logger) {
         mContext = context;
         mLockscreenUserManager = lockscreenUserManager;
         mSmartReplyController = smartReplyController;
         mEntryManager = notificationEntryManager;
         mStatusBarLazy = statusBarLazy;
         mMainHandler = mainHandler;
+        mLogger = logger;
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index f0fed13..b08eb9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -25,6 +25,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.media.MediaDataManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.ActionClickLogger;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.MediaArtworkProcessor;
 import com.android.systemui.statusbar.NotificationListener;
@@ -73,7 +74,8 @@
             Lazy<StatusBar> statusBarLazy,
             StatusBarStateController statusBarStateController,
             Handler mainHandler,
-            RemoteInputUriController remoteInputUriController) {
+            RemoteInputUriController remoteInputUriController,
+            ActionClickLogger actionClickLogger) {
         return new NotificationRemoteInputManager(
                 context,
                 lockscreenUserManager,
@@ -82,7 +84,8 @@
                 statusBarLazy,
                 statusBarStateController,
                 mainHandler,
-                remoteInputUriController);
+                remoteInputUriController,
+                actionClickLogger);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 428de9e..669e6a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -37,6 +37,7 @@
 import com.android.systemui.ActivityIntentHelper;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.ActionClickLogger;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -73,6 +74,7 @@
     private View mPendingRemoteInputView;
     private KeyguardManager mKeyguardManager;
     private final CommandQueue mCommandQueue;
+    private final ActionClickLogger mActionClickLogger;
     private int mDisabled2;
     protected BroadcastReceiver mChallengeReceiver = new ChallengeReceiver();
     private Handler mMainHandler = new Handler();
@@ -87,7 +89,8 @@
             StatusBarStateController statusBarStateController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             ActivityStarter activityStarter, ShadeController shadeController,
-            CommandQueue commandQueue) {
+            CommandQueue commandQueue,
+            ActionClickLogger clickLogger) {
         mContext = context;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mShadeController = shadeController;
@@ -101,6 +104,7 @@
         mKeyguardManager = context.getSystemService(KeyguardManager.class);
         mCommandQueue = commandQueue;
         mCommandQueue.addCallback(this);
+        mActionClickLogger = clickLogger;
         mActivityIntentHelper = new ActivityIntentHelper(mContext);
         mGroupManager = groupManager;
         // Listen to onKeyguardShowingChanged in case a managed profile needs to be unlocked
@@ -304,9 +308,12 @@
             NotificationRemoteInputManager.ClickHandler defaultHandler) {
         final boolean isActivity = pendingIntent.isActivity();
         if (isActivity) {
+            mActionClickLogger.logWaitingToCloseKeyguard(pendingIntent);
             final boolean afterKeyguardGone = mActivityIntentHelper.wouldLaunchResolverActivity(
                     pendingIntent.getIntent(), mLockscreenUserManager.getCurrentUserId());
             mActivityStarter.dismissKeyguardThenExecute(() -> {
+                mActionClickLogger.logKeyguardGone(pendingIntent);
+
                 try {
                     ActivityManager.getService().resumeAppSwitches();
                 } catch (RemoteException e) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index 1117646..5a7dea4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -82,7 +82,8 @@
                 () -> mock(StatusBar.class),
                 mStateController,
                 Handler.createAsync(Looper.myLooper()),
-                mRemoteInputUriController);
+                mRemoteInputUriController,
+                mock(ActionClickLogger.class));
         mEntry = new NotificationEntryBuilder()
                 .setPkg(TEST_PACKAGE_NAME)
                 .setOpPkg(TEST_PACKAGE_NAME)
@@ -256,17 +257,26 @@
 
     private class TestableNotificationRemoteInputManager extends NotificationRemoteInputManager {
 
-        TestableNotificationRemoteInputManager(Context context,
+        TestableNotificationRemoteInputManager(
+                Context context,
                 NotificationLockscreenUserManager lockscreenUserManager,
                 SmartReplyController smartReplyController,
                 NotificationEntryManager notificationEntryManager,
                 Lazy<StatusBar> statusBarLazy,
                 StatusBarStateController statusBarStateController,
                 Handler mainHandler,
-                RemoteInputUriController remoteInputUriController) {
-            super(context, lockscreenUserManager, smartReplyController, notificationEntryManager,
-                    statusBarLazy, statusBarStateController, mainHandler,
-                    remoteInputUriController);
+                RemoteInputUriController remoteInputUriController,
+                ActionClickLogger actionClickLogger) {
+            super(
+                    context,
+                    lockscreenUserManager,
+                    smartReplyController,
+                    notificationEntryManager,
+                    statusBarLazy,
+                    statusBarStateController,
+                    mainHandler,
+                    remoteInputUriController,
+                    actionClickLogger);
         }
 
         public void setUpWithPresenterForTest(Callback callback,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index 22dc080..79507e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -92,7 +92,8 @@
                 mNotificationEntryManager, () -> mock(StatusBar.class),
                 mStatusBarStateController,
                 Handler.createAsync(Looper.myLooper()),
-                mRemoteInputUriController);
+                mRemoteInputUriController,
+                mock(ActionClickLogger.class));
         mRemoteInputManager.setUpWithCallback(mCallback, mDelegate);
         mNotification = new Notification.Builder(mContext, "")
                 .setSmallIcon(R.drawable.ic_person)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index cd2c349..bf2bd38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -31,6 +31,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.ActionClickLogger;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -73,7 +74,8 @@
         mRemoteInputCallback = spy(new StatusBarRemoteInputCallback(mContext,
                 mock(NotificationGroupManager.class), mNotificationLockscreenUserManager,
                 mKeyguardStateController, mStatusBarStateController, mStatusBarKeyguardViewManager,
-                mActivityStarter, mShadeController, new CommandQueue(mContext)));
+                mActivityStarter, mShadeController, new CommandQueue(mContext),
+                mock(ActionClickLogger.class)));
         mRemoteInputCallback.mChallengeReceiver = mRemoteInputCallback.new ChallengeReceiver();
     }
 
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 4f5a02a..2a65b33 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -201,7 +201,6 @@
         private SurfaceControl.DisplayConfig[] mDisplayConfigs;
         private Spline mSystemBrightnessToNits;
         private Spline mNitsToHalBrightness;
-        private boolean mHalBrightnessSupport;
 
         private DisplayDeviceConfig mDisplayDeviceConfig;
 
@@ -225,7 +224,6 @@
             }
             mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken);
             mGameContentTypeSupported = SurfaceControl.getGameContentTypeSupport(displayToken);
-            mHalBrightnessSupport = SurfaceControl.getDisplayBrightnessSupport(displayToken);
             mDisplayDeviceConfig = null;
             // Defer configuration file loading
             BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
@@ -717,11 +715,10 @@
                         Trace.traceBegin(Trace.TRACE_TAG_POWER, "setDisplayBrightness("
                                 + "id=" + physicalDisplayId + ", brightness=" + brightness + ")");
                         try {
-                            // TODO: make it float
                             if (isHalBrightnessRangeSpecified()) {
                                 brightness = displayBrightnessToHalBrightness(
-                                        BrightnessSynchronizer.brightnessFloatToInt(getContext(),
-                                                brightness));
+                                        BrightnessSynchronizer.brightnessFloatToIntRange(
+                                                getContext(), brightness));
                             }
                             if (mBacklight != null) {
                                 mBacklight.setBrightness(brightness);
@@ -744,12 +741,13 @@
                      * Hal brightness space if the HAL brightness space has been provided via
                      * a display device configuration file.
                      */
-                    private float displayBrightnessToHalBrightness(int brightness) {
+                    private float displayBrightnessToHalBrightness(float brightness) {
                         if (!isHalBrightnessRangeSpecified()) {
                             return PowerManager.BRIGHTNESS_INVALID_FLOAT;
                         }
 
-                        if (brightness == 0) {
+                        if (BrightnessSynchronizer.floatEquals(
+                                brightness, PowerManager.BRIGHTNESS_OFF)) {
                             return PowerManager.BRIGHTNESS_OFF_FLOAT;
                         }
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 549e336..9de95ab 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -2208,8 +2208,12 @@
         public void setHdmiCecVolumeControlEnabled(final boolean isHdmiCecVolumeControlEnabled) {
             enforceAccessPermission();
             long token = Binder.clearCallingIdentity();
-            HdmiControlService.this.setHdmiCecVolumeControlEnabled(isHdmiCecVolumeControlEnabled);
-            Binder.restoreCallingIdentity(token);
+            try {
+                HdmiControlService.this.setHdmiCecVolumeControlEnabled(
+                        isHdmiCecVolumeControlEnabled);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 91c849c..0598680 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6684,6 +6684,13 @@
     }
 
     @Override
+    void getAnimationPosition(Point outPosition) {
+        // Always animate from zero because if the activity doesn't fill the task, the letterbox
+        // will fill the remaining area that should be included in the animation.
+        outPosition.set(0, 0);
+    }
+
+    @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         if (mCompatDisplayInsets != null) {
             Configuration overrideConfig = getRequestedOverrideConfiguration();
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 70488b4..2f868d9 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -1311,8 +1311,17 @@
     /**
      * Make sure that all activities that need to be visible in the stack (that is, they
      * currently can be seen by the user) actually are and update their configuration.
+     * @param starting The top most activity in the task.
+     *                 The activity is either starting or resuming.
+     *                 Caller should ensure starting activity is visible.
+     * @param preserveWindows Flag indicating whether windows should be preserved when updating
+     *                        configuration in {@link mEnsureActivitiesVisibleHelper}.
+     * @param configChanges Parts of the configuration that changed for this activity for evaluating
+     *                      if the screen should be frozen as part of
+     *                      {@link mEnsureActivitiesVisibleHelper}.
+     *
      */
-    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
+    void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
             boolean preserveWindows) {
         ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
     }
@@ -1321,9 +1330,19 @@
      * Ensure visibility with an option to also update the configuration of visible activities.
      * @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
      * @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
+     * @param starting The top most activity in the task.
+     *                 The activity is either starting or resuming.
+     *                 Caller should ensure starting activity is visible.
+     * @param notifyClients Flag indicating whether the visibility updates should be sent to the
+     *                      clients in {@link mEnsureActivitiesVisibleHelper}.
+     * @param preserveWindows Flag indicating whether windows should be preserved when updating
+     *                        configuration in {@link mEnsureActivitiesVisibleHelper}.
+     * @param configChanges Parts of the configuration that changed for this activity for evaluating
+     *                      if the screen should be frozen as part of
+     *                      {@link mEnsureActivitiesVisibleHelper}.
      */
     // TODO: Should be re-worked based on the fact that each task as a stack in most cases.
-    void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
+    void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
             boolean preserveWindows, boolean notifyClients) {
         mTopActivityOccludesKeyguard = false;
         mTopDismissingKeyguardActivity = null;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 79e8ee3..bcdd6e3 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1539,7 +1539,10 @@
      *
      * Note: This method should only be called from {@link #startActivityUnchecked}.
      */
-    private int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
+
+    // TODO(b/152429287): Make it easier to exercise code paths through startActivityInner
+    @VisibleForTesting
+    int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             int startFlags, boolean doResume, ActivityOptions options, Task inTask,
             boolean restrictedBgActivity) {
@@ -1660,7 +1663,10 @@
                 // Also, we don't want to resume activities in a task that currently has an overlay
                 // as the starting activity just needs to be in the visible paused state until the
                 // over is removed.
-                mTargetStack.ensureActivitiesVisible(mStartActivity, 0, !PRESERVE_WINDOWS);
+                // Passing {@code null} as the start parameter ensures all activities are made
+                // visible.
+                mTargetStack.ensureActivitiesVisible(null /* starting */,
+                        0 /* configChanges */, !PRESERVE_WINDOWS);
                 // Go ahead and tell window manager to execute app transition for this activity
                 // since the app transition will not be triggered through the resume channel.
                 mTargetStack.getDisplay().mDisplayContent.executeAppTransition();
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index c92de2b..c4e03f5 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -21,6 +21,7 @@
 import static com.android.server.wm.ActivityStack.TAG_VISIBILITY;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
 
+import android.annotation.Nullable;
 import android.util.Slog;
 
 import com.android.internal.util.function.pooled.PooledConsumer;
@@ -42,6 +43,16 @@
         mContiner = container;
     }
 
+    /**
+     * Update all attributes except {@link mContiner} to use in subsequent calculations.
+     *
+     * @param starting The activity that is being started
+     * @param configChanges Parts of the configuration that changed for this activity for evaluating
+     *                      if the screen should be frozen.
+     * @param preserveWindows Flag indicating whether windows should be preserved when updating.
+     * @param notifyClients Flag indicating whether the configuration and visibility changes shoulc
+     *                      be sent to the clients.
+     */
     void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
             boolean notifyClients) {
         mStarting = starting;
@@ -60,8 +71,17 @@
      * Ensure visibility with an option to also update the configuration of visible activities.
      * @see ActivityStack#ensureActivitiesVisible(ActivityRecord, int, boolean)
      * @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
+     * @param starting The top most activity in the task.
+     *                 The activity is either starting or resuming.
+     *                 Caller should ensure starting activity is visible.
+     *
+     * @param configChanges Parts of the configuration that changed for this activity for evaluating
+     *                      if the screen should be frozen.
+     * @param preserveWindows Flag indicating whether windows should be preserved when updating.
+     * @param notifyClients Flag indicating whether the configuration and visibility changes shoulc
+     *                      be sent to the clients.
      */
-    void process(ActivityRecord starting, int configChanges, boolean preserveWindows,
+    void process(@Nullable ActivityRecord starting, int configChanges, boolean preserveWindows,
             boolean notifyClients) {
         reset(starting, configChanges, preserveWindows, notifyClients);
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5d7ec12..7bfddd7 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2120,6 +2120,11 @@
         return getBounds();
     }
 
+    /** Gets the position relative to parent for animation. */
+    void getAnimationPosition(Point outPosition) {
+        getRelativePosition(outPosition);
+    }
+
     /**
      * Applies the app transition animation according the given the layout properties in the
      * window hierarchy.
@@ -2178,9 +2183,9 @@
 
         // Separate position and size for use in animators.
         mTmpRect.set(getAnimationBounds(appStackClipMode));
-        if (sHierarchicalAnimations) {
-            getRelativePosition(mTmpPoint);
-        } else {
+        getAnimationPosition(mTmpPoint);
+        if (!sHierarchicalAnimations) {
+            // Non-hierarchical animation uses position in global coordinates.
             mTmpPoint.set(mTmpRect.left, mTmpRect.top);
         }
         mTmpRect.offsetTo(0, 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index edc9756..02d1f9b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -49,6 +49,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
@@ -56,10 +57,10 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyObject;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 
@@ -99,7 +100,6 @@
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class ActivityStarterTests extends ActivityTestsBase {
-    private ActivityStarter mStarter;
     private ActivityStartController mController;
     private ActivityMetricsLogger mActivityMetricsLogger;
     private PackageManagerInternal mMockPackageManager;
@@ -127,8 +127,6 @@
         mController = mock(ActivityStartController.class);
         mActivityMetricsLogger = mock(ActivityMetricsLogger.class);
         clearInvocations(mActivityMetricsLogger);
-        mStarter = new ActivityStarter(mController, mService, mService.mStackSupervisor,
-                mock(ActivityStartInterceptor.class));
     }
 
     @Test
@@ -181,6 +179,7 @@
      * {@link ActivityStarter#execute} based on these preconditions and ensures the result matches
      * the expected. It is important to note that the method also checks side effects of the start,
      * such as ensuring {@link ActivityOptions#abort()} is called in the relevant scenarios.
+     *
      * @param preconditions A bitmask representing the preconditions for the launch
      * @param launchFlags The launch flags to be provided by the launch {@link Intent}.
      * @param expectedResult The expected result from the launch.
@@ -202,7 +201,7 @@
         final WindowProcessController wpc =
                 containsConditions(preconditions, PRECONDITION_NO_CALLER_APP)
                 ? null : new WindowProcessController(service, ai, null, 0, -1, null, listener);
-        doReturn(wpc).when(service).getProcessController(anyObject());
+        doReturn(wpc).when(service).getProcessController(any());
 
         final Intent intent = new Intent();
         intent.setFlags(launchFlags);
@@ -1034,4 +1033,46 @@
 
         verify(recentTasks, times(1)).add(any());
     }
+
+    @Test
+    public void testStartActivityInner_allSplitScreenPrimaryActivitiesVisible() {
+        // Given
+        final ActivityStarter starter = prepareStarter(0, false);
+
+        starter.setReason("testAllSplitScreenPrimaryActivitiesAreResumed");
+
+        final ActivityRecord targetRecord = new ActivityBuilder(mService).build();
+        targetRecord.setFocusable(false);
+        targetRecord.setVisibility(false);
+        final ActivityRecord sourceRecord = new ActivityBuilder(mService).build();
+
+        final ActivityStack stack = spy(
+                mRootWindowContainer.getDefaultTaskDisplayArea()
+                        .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
+                                /* onTop */true));
+
+        stack.addChild(targetRecord);
+
+        doReturn(stack).when(mRootWindowContainer)
+                .getLaunchStack(any(), any(), any(), anyBoolean(), any(), anyInt(), anyInt());
+
+        starter.mStartActivity = new ActivityBuilder(mService).build();
+
+        // When
+        starter.startActivityInner(
+                /* r */targetRecord,
+                /* sourceRecord */ sourceRecord,
+                /* voiceSession */null,
+                /* voiceInteractor */ null,
+                /* startFlags */ 0,
+                /* doResume */true,
+                /* options */null,
+                /* inTask */null,
+                /* restrictedBgActivity */false);
+
+        // Then
+        verify(stack).ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
+        verify(targetRecord).makeVisibleIfNeeded(null, true);
+        assertTrue(targetRecord.mVisibleRequested);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index 8f18f64..07050d9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -52,6 +52,7 @@
 import static org.mockito.Mockito.verify;
 
 import android.content.res.Configuration;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.view.IWindowManager;
@@ -432,20 +433,35 @@
         removeGlobalMinSizeRestriction();
         final Rect stackBounds = new Rect(0, 0, 1000, 600);
         final Rect taskBounds = new Rect(100, 400, 600, 800);
-        mStack.setBounds(stackBounds);
-        mTask.setBounds(taskBounds);
+        // Set the bounds and windowing mode to window configuration directly, otherwise the
+        // testing setups may be discarded by configuration resolving.
+        mStack.getWindowConfiguration().setBounds(stackBounds);
+        mTask.getWindowConfiguration().setBounds(taskBounds);
+        mActivity.getWindowConfiguration().setBounds(taskBounds);
 
         // Check that anim bounds for freeform window match task bounds
-        mTask.setWindowingMode(WINDOWING_MODE_FREEFORM);
+        mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FREEFORM);
         assertEquals(mTask.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_NONE));
 
         // STACK_CLIP_AFTER_ANIM should use task bounds since they will be clipped by
         // bounds animation layer.
-        mTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         assertEquals(mTask.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_AFTER_ANIM));
 
+        // Even the activity is smaller than task and it is not aligned to the top-left corner of
+        // task, the animation bounds the same as task and position should be zero because in real
+        // case the letterbox will fill the remaining area in task.
+        final Rect halfBounds = new Rect(taskBounds);
+        halfBounds.scale(0.5f);
+        mActivity.getWindowConfiguration().setBounds(halfBounds);
+        final Point animationPosition = new Point();
+        mActivity.getAnimationPosition(animationPosition);
+
+        assertEquals(taskBounds, mActivity.getAnimationBounds(STACK_CLIP_AFTER_ANIM));
+        assertEquals(new Point(0, 0), animationPosition);
+
         // STACK_CLIP_BEFORE_ANIM should use stack bounds since it won't be clipped later.
-        mTask.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        mTask.getWindowConfiguration().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         assertEquals(mStack.getBounds(), mActivity.getAnimationBounds(STACK_CLIP_BEFORE_ANIM));
     }