Merge "Fix incorrect escape in framework string"
diff --git a/Android.bp b/Android.bp
index b8c060d..9127966f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -124,7 +124,6 @@
         ":libbluetooth-binder-aidl",
         ":libcamera_client_aidl",
         ":libcamera_client_framework_aidl",
-        ":packagemanager_aidl",
         ":libupdate_engine_aidl",
         ":resourcemanager_aidl",
         ":storaged_aidl",
@@ -228,6 +227,7 @@
     name: "framework-internal-utils",
     static_libs: [
         "apex_aidl_interface-java",
+        "packagemanager_aidl-java",
         "framework-protos",
         "updatable-driver-protos",
         "ota_metadata_proto_java",
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 546abf8..ffac986 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4918,6 +4918,12 @@
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_CAR_LAUNCHER = "android.intent.category.CAR_LAUNCHER";
     /**
+     * Used to indicate that the activity can be used in communal mode.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_COMMUNAL_MODE = "android.intent.category.COMMUNAL_MODE";
+    /**
      * Indicates a Leanback settings activity to be displayed in the Leanback launcher.
      * @hide
      */
diff --git a/core/java/android/os/ExternalVibration.java b/core/java/android/os/ExternalVibration.java
index 7fd02116..0686dd6 100644
--- a/core/java/android/os/ExternalVibration.java
+++ b/core/java/android/os/ExternalVibration.java
@@ -42,25 +42,38 @@
     // boundaries.
     @NonNull
     private IBinder mToken;
-
     public ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs,
             @NonNull IExternalVibrationController controller) {
+        this(uid, pkg, attrs, controller, new Binder());
+    }
+
+    private ExternalVibration(int uid, @NonNull String pkg, @NonNull AudioAttributes attrs,
+            @NonNull IExternalVibrationController controller, @NonNull IBinder token) {
         mUid = uid;
         mPkg = Preconditions.checkNotNull(pkg);
         mAttrs = Preconditions.checkNotNull(attrs);
         mController = Preconditions.checkNotNull(controller);
-        mToken = new Binder();
+        mToken = Preconditions.checkNotNull(token);
+
+        // IExternalVibrationController is a hidden AIDL interface with implementation provided by
+        // the audio framework to allow mute/unmute control over the external vibration.
+        //
+        // Transactions are locked in audioflinger, and should be blocking to avoid racing
+        // conditions on multiple audio playback.
+        //
+        // They can also be triggered before starting a new external vibration in
+        // IExternalVibratorService, as the ongoing external vibration needs to be muted before the
+        // new one can start, which also requires blocking calls to mute.
+        Binder.allowBlocking(mController.asBinder());
     }
 
     private ExternalVibration(Parcel in) {
-        mUid = in.readInt();
-        mPkg = in.readString();
-        mAttrs = readAudioAttributes(in);
-        mController = IExternalVibrationController.Stub.asInterface(in.readStrongBinder());
-        mToken = in.readStrongBinder();
+        this(in.readInt(), in.readString(), readAudioAttributes(in),
+                IExternalVibrationController.Stub.asInterface(in.readStrongBinder()),
+                in.readStrongBinder());
     }
 
-    private AudioAttributes readAudioAttributes(Parcel in) {
+    private static AudioAttributes readAudioAttributes(Parcel in) {
         int usage = in.readInt();
         int contentType = in.readInt();
         int capturePreset = in.readInt();
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
index 179ac8b..a611d65d 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityStatsLogUtils.java
@@ -39,6 +39,9 @@
 import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_FULL_SCREEN;
 import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_UNKNOWN_MODE;
 import static com.android.internal.util.FrameworkStatsLog.MAGNIFICATION_USAGE_REPORTED__ACTIVATED_MODE__MAGNIFICATION_WINDOW;
+import static com.android.internal.util.FrameworkStatsLog.NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_CLICKED;
+import static com.android.internal.util.FrameworkStatsLog.NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_SERVICE_DISABLED;
+import static com.android.internal.util.FrameworkStatsLog.NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_SHOWN;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -50,6 +53,16 @@
 
 /** Methods for logging accessibility states. */
 public final class AccessibilityStatsLogUtils {
+    /** The status represents an accessibility privacy warning has been shown. */
+    public static int ACCESSIBILITY_PRIVACY_WARNING_STATUS_SHOWN =
+            NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_SHOWN;
+    /** The status represents an accessibility privacy warning has been clicked to review. */
+    public static int ACCESSIBILITY_PRIVACY_WARNING_STATUS_CLICKED =
+            NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_CLICKED;
+    /** The status represents an accessibility privacy warning service has been disabled. */
+    public static int ACCESSIBILITY_PRIVACY_WARNING_STATUS_SERVICE_DISABLED =
+            NON_A11Y_TOOL_SERVICE_WARNING_REPORTED__STATUS__WARNING_SERVICE_DISABLED;
+
     private static final int UNKNOWN_STATUS =
             ACCESSIBILITY_SHORTCUT_REPORTED__SERVICE_STATUS__UNKNOWN;
 
@@ -154,6 +167,23 @@
                 convertToLoggingMagnificationMode(mode));
     }
 
+    /**
+     * Logs the warning status of the non-a11yTool service. Calls this when the warning status is
+     * changed.
+     *
+     * @param packageName    The package name of the non-a11yTool service
+     * @param status         The warning status of the non-a11yTool service, it should be one of
+     *                       {@code ACCESSIBILITY_PRIVACY_WARNING_STATUS_SHOWN},{@code
+     *                       ACCESSIBILITY_PRIVACY_WARNING_STATUS_CLICKED} and {@code
+     *                       ACCESSIBILITY_PRIVACY_WARNING_STATUS_SERVICE_DISABLED}
+     * @param durationMillis The duration in milliseconds between current and previous status
+     */
+    public static void logNonA11yToolServiceWarningReported(String packageName, int status,
+            long durationMillis) {
+        FrameworkStatsLog.write(FrameworkStatsLog.NON_A11Y_TOOL_SERVICE_WARNING_REPORT,
+                packageName, status, durationMillis);
+    }
+
     private static boolean isAccessibilityFloatingMenuEnabled(Context context) {
         return Settings.Secure.getInt(context.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_BUTTON_MODE, /* def= */ -1)
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 2d0b3f9..74211fb 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -43,6 +43,7 @@
 import com.android.internal.view.IInputContext;
 
 import java.lang.ref.WeakReference;
+import java.util.function.Supplier;
 
 /**
  * Takes care of remote method invocations of {@link InputConnection} in the IME client side.
@@ -222,9 +223,7 @@
     @Override
     public void getTextAfterCursor(int length, int flags,
             AndroidFuture future /* T=CharSequence */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<CharSequence> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextAfterCursor");
             try {
                 final InputConnection ic = getInputConnection();
@@ -241,7 +240,7 @@
                     ImeTracing.getInstance().triggerClientDump(
                             TAG + "#getTextAfterCursor", mParentInputMethodManager, icProto);
                 }
-                typedFuture.complete(result);
+                return result;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -251,9 +250,7 @@
     @Override
     public void getTextBeforeCursor(int length, int flags,
             AndroidFuture future /* T=CharSequence */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<CharSequence> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getTextBeforeCursor");
             try {
                 final InputConnection ic = getInputConnection();
@@ -270,7 +267,7 @@
                     ImeTracing.getInstance().triggerClientDump(
                             TAG + "#getTextBeforeCursor", mParentInputMethodManager, icProto);
                 }
-                typedFuture.complete(result);
+                return result;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -279,9 +276,7 @@
 
     @Override
     public void getSelectedText(int flags, AndroidFuture future /* T=CharSequence */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<CharSequence> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSelectedText");
             try {
                 final InputConnection ic = getInputConnection();
@@ -298,7 +293,7 @@
                     ImeTracing.getInstance().triggerClientDump(
                             TAG + "#getSelectedText", mParentInputMethodManager, icProto);
                 }
-                typedFuture.complete(result);
+                return result;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -308,9 +303,7 @@
     @Override
     public void getSurroundingText(int beforeLength, int afterLength, int flags,
             AndroidFuture future /* T=SurroundingText */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<SurroundingText> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getSurroundingText");
             try {
                 final InputConnection ic = getInputConnection();
@@ -327,7 +320,7 @@
                     ImeTracing.getInstance().triggerClientDump(
                             TAG + "#getSurroundingText", mParentInputMethodManager, icProto);
                 }
-                typedFuture.complete(result);
+                return result;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -336,9 +329,7 @@
 
     @Override
     public void getCursorCapsMode(int reqModes, AndroidFuture future /* T=Integer */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<Integer> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getCursorCapsMode");
             try {
                 final InputConnection ic = getInputConnection();
@@ -355,7 +346,7 @@
                     ImeTracing.getInstance().triggerClientDump(
                             TAG + "#getCursorCapsMode", mParentInputMethodManager, icProto);
                 }
-                typedFuture.complete(result);
+                return result;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -365,9 +356,7 @@
     @Override
     public void getExtractedText(ExtractedTextRequest request, int flags,
             AndroidFuture future /* T=ExtractedText */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<ExtractedText> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#getExtractedText");
             try {
                 final InputConnection ic = getInputConnection();
@@ -384,7 +373,7 @@
                     ImeTracing.getInstance().triggerClientDump(
                             TAG + "#getExtractedText", mParentInputMethodManager, icProto);
                 }
-                typedFuture.complete(result);
+                return result;
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -695,20 +684,15 @@
 
     @Override
     public void requestCursorUpdates(int cursorUpdateMode, AndroidFuture future /* T=Boolean */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<Boolean> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#requestCursorUpdates");
             try {
                 final InputConnection ic = getInputConnection();
-                final boolean result;
                 if (ic == null || !isActive()) {
                     Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection");
-                    result = false;
-                } else {
-                    result = ic.requestCursorUpdates(cursorUpdateMode);
+                    return false;
                 }
-                typedFuture.complete(result);
+                return ic.requestCursorUpdates(cursorUpdateMode);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -718,26 +702,19 @@
     @Override
     public void commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts,
             AndroidFuture future /* T=Boolean */) {
-        @SuppressWarnings("unchecked")
-        final AndroidFuture<Boolean> typedFuture = future;
-        dispatch(() -> {
+        dispatch(future, () -> {
             Trace.traceBegin(Trace.TRACE_TAG_INPUT, "InputConnection#commitContent");
             try {
                 final InputConnection ic = getInputConnection();
-                final boolean result;
                 if (ic == null || !isActive()) {
                     Log.w(TAG, "commitContent on inactive InputConnection");
-                    result = false;
-                } else {
-                    if (inputContentInfo == null || !inputContentInfo.validate()) {
-                        Log.w(TAG, "commitContent with invalid inputContentInfo="
-                                + inputContentInfo);
-                        result = false;
-                    } else {
-                        result = ic.commitContent(inputContentInfo, flags, opts);
-                    }
+                    return false;
                 }
-                typedFuture.complete(result);
+                if (inputContentInfo == null || !inputContentInfo.validate()) {
+                    Log.w(TAG, "commitContent with invalid inputContentInfo=" + inputContentInfo);
+                    return false;
+                }
+                return ic.commitContent(inputContentInfo, flags, opts);
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_INPUT);
             }
@@ -771,4 +748,10 @@
 
         mH.post(runnable);
     }
+
+    private <T> void dispatch(@NonNull AndroidFuture untypedFuture, @NonNull Supplier<T> supplier) {
+        @SuppressWarnings("unchecked")
+        final AndroidFuture<T> future = untypedFuture;
+        dispatch(() -> future.complete(supplier.get()));
+    }
 }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7fe4b65..8813c43 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -660,6 +660,9 @@
     <!-- Indicates whether to enable an animation when unfolding a device or not -->
     <bool name="config_unfoldTransitionEnabled">false</bool>
 
+    <!-- Indicates whether to enable hinge angle sensor when using unfold animation -->
+    <bool name="config_unfoldTransitionHingeAngle">false</bool>
+
     <!-- Indicates that the device supports having more than one internal display on at the same
          time. Only applicable to devices with more than one internal display. If this option is
          set to false, DisplayManager will make additional effort to ensure no more than 1 internal
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d2c6f78..d419f80 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4557,7 +4557,7 @@
     <string name="accessibility_shortcut_multiple_service_warning">Holding down both volume keys for a few seconds turns on accessibility features. This may change how your device works.\n\nCurrent features:\n<xliff:g id="service" example="TalkBack">%1$s</xliff:g>\nYou can change selected features in Settings > Accessibility.</string>
 
     <!-- Used in multiple service warning to list current features. [CHAR LIMIT=none] -->
-    <string name="accessibility_shortcut_multiple_service_list">\t• <xliff:g id="service" example="TalkBack">%1$s</xliff:g>\n</string>
+    <string name="accessibility_shortcut_multiple_service_list">\u0020• <xliff:g id="service" example="TalkBack">%1$s</xliff:g>\n</string>
 
     <!-- Dialog title for dialog shown when this accessibility shortcut is activated, and we want to confirm that the user understands what's going to happen. [CHAR LIMIT=none] -->
     <string name="accessibility_shortcut_single_service_warning_title">Turn on <xliff:g id="service" example="TalkBack">%1$s</xliff:g> shortcut?</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 527b928..b2f7e82 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3844,6 +3844,7 @@
   <java-symbol type="string" name="config_foldedArea" />
   <java-symbol type="bool" name="config_supportsConcurrentInternalDisplays" />
   <java-symbol type="bool" name="config_unfoldTransitionEnabled" />
+  <java-symbol type="bool" name="config_unfoldTransitionHingeAngle" />
   <java-symbol type="array" name="config_perDeviceStateRotationLockDefaults" />
 
 
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index 72cddc9..353556d 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -50,6 +50,13 @@
     private final Context mContext;
     private final Map<Callback, CallbackRecord> mCallbacks;
 
+    /**
+     * Store the WindowContext in a field. If it is a local variable, and it is garbage collected
+     * during a MediaProjection session, the WindowContainer listener no longer exists.
+     */
+    @Nullable
+    private Context mWindowContext;
+
     /** @hide */
     public MediaProjection(Context context, IMediaProjection impl) {
         mCallbacks = new ArrayMap<Callback, CallbackRecord>();
@@ -162,11 +169,11 @@
      */
     private VirtualDisplayConfig.Builder buildMirroredVirtualDisplay(@NonNull String name,
             int width, int height, int dpi) {
-        Context windowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(),
+        mWindowContext = mContext.createWindowContext(mContext.getDisplayNoVerify(),
                 TYPE_APPLICATION, null /* options */);
         final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
                 height, dpi);
-        builder.setWindowTokenClientToMirror(windowContext.getWindowContextToken());
+        builder.setWindowTokenClientToMirror(mWindowContext.getWindowContextToken());
         return builder;
     }
 
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
index cc44b5e..b1e8c38 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_date_privacy.xml
@@ -49,6 +49,11 @@
         />
     </FrameLayout>
 
+    <!-- We want this to be centered (to align with notches). In order to do that, the following
+         has to hold (in portrait):
+         * date_container and privacy_container must have the same width and weight
+         * header_text_container must be gone
+         -->
     <android.widget.Space
         android:id="@+id/space"
         android:layout_width="0dp"
@@ -75,7 +80,7 @@
         android:layout_weight="1"
         android:gravity="center_vertical|end" >
 
-    <include layout="@layout/ongoing_privacy_chip" />
+        <include layout="@layout/ongoing_privacy_chip" />
 
     </FrameLayout>
 </LinearLayout>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
index 9b5eae8..49cd279 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider
 import com.android.systemui.unfold.updates.DeviceFoldStateProvider
 import com.android.systemui.unfold.updates.hinge.EmptyHingeAngleProvider
+import com.android.systemui.unfold.updates.hinge.HingeSensorAngleProvider
 import com.android.systemui.unfold.updates.hinge.RotationSensorHingeAngleProvider
 import java.lang.IllegalStateException
 import java.util.concurrent.Executor
@@ -50,7 +51,13 @@
 
     val hingeAngleProvider =
         if (config.mode == ANIMATION_MODE_HINGE_ANGLE) {
-            RotationSensorHingeAngleProvider(sensorManager)
+            // TODO: after removing temporary "config.mode" we should just
+            //       switch between fixed timing and hinge sensor based on this flag
+            if (config.isHingeAngleEnabled) {
+                HingeSensorAngleProvider(sensorManager)
+            } else {
+                RotationSensorHingeAngleProvider(sensorManager)
+            }
         } else {
             EmptyHingeAngleProvider()
         }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
index fa6b5de..e7c6998a 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
@@ -25,6 +25,9 @@
     override val isEnabled: Boolean
         get() = readIsEnabled() && mode != ANIMATION_MODE_DISABLED
 
+    override val isHingeAngleEnabled: Boolean
+        get() = readIsHingeAngleEnabled()
+
     @AnimationMode
     override val mode: Int
         get() = SystemProperties.getInt(UNFOLD_TRANSITION_MODE_PROPERTY_NAME,
@@ -32,6 +35,9 @@
 
     private fun readIsEnabled(): Boolean = context.resources
         .getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled)
+
+    private fun readIsHingeAngleEnabled(): Boolean = context.resources
+        .getBoolean(com.android.internal.R.bool.config_unfoldTransitionHingeAngle)
 }
 
 /**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
index 75d9dc3..a569757 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt
@@ -19,6 +19,7 @@
 
 interface UnfoldTransitionConfig {
     val isEnabled: Boolean
+    val isHingeAngleEnabled: Boolean
 
     @AnimationMode
     val mode: Int
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
index b111892..10e6c2b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
@@ -16,6 +16,7 @@
 package com.android.systemui.unfold.progress
 
 import android.os.Handler
+import android.util.MathUtils.saturate
 import androidx.dynamicanimation.animation.DynamicAnimation
 import androidx.dynamicanimation.animation.FloatPropertyCompat
 import androidx.dynamicanimation.animation.SpringAnimation
@@ -24,6 +25,7 @@
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
 import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED
 import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN
+import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING
 import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE
 import com.android.systemui.unfold.updates.FoldStateProvider
 import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate
@@ -33,7 +35,6 @@
  * Maps fold updates to unfold transition progress using DynamicAnimation.
  *
  * TODO(b/193793338) Current limitations:
- *  - doesn't handle folding transition
  *  - doesn't handle postures
  */
 internal class PhysicsBasedUnfoldTransitionProgressProvider(
@@ -75,14 +76,20 @@
 
     override fun onHingeAngleUpdate(angle: Float) {
         if (!isTransitionRunning || isAnimatedCancelRunning) return
-        springAnimation.animateToFinalPosition(angle / 180f)
+        val progress = saturate(angle / FINAL_HINGE_ANGLE_POSITION)
+        springAnimation.animateToFinalPosition(progress)
     }
 
     override fun onFoldUpdate(@FoldUpdate update: Int) {
         when (update) {
             FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> {
-                onStartTransition()
                 startTransition(startValue = 0f)
+
+                // Stop the animation if the device has already opened by the time when
+                // the display is available as we won't receive the full open event anymore
+                if (foldStateProvider.isFullyOpened) {
+                    cancelTransition(endValue = 1f, animate = true)
+                }
             }
             FOLD_UPDATE_FINISH_FULL_OPEN -> {
                 cancelTransition(endValue = 1f, animate = true)
@@ -90,13 +97,16 @@
             FOLD_UPDATE_FINISH_CLOSED -> {
                 cancelTransition(endValue = 0f, animate = false)
             }
+            FOLD_UPDATE_START_CLOSING -> {
+                startTransition(startValue = 1f)
+            }
         }
     }
 
     private fun cancelTransition(endValue: Float, animate: Boolean) {
         handler.removeCallbacks(timeoutRunnable)
 
-        if (animate) {
+        if (isTransitionRunning && animate) {
             isAnimatedCancelRunning = true
             springAnimation.animateToFinalPosition(endValue)
         } else {
@@ -182,3 +192,4 @@
 private const val TRANSITION_TIMEOUT_MILLIS = 2000L
 private const val SPRING_STIFFNESS = 200.0f
 private const val MINIMAL_VISIBLE_CHANGE = 0.001f
+private const val FINAL_HINGE_ANGLE_POSITION = 165f
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
index 949652b..c37ab06 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
@@ -68,20 +68,29 @@
         outputListeners.remove(listener)
     }
 
+    override val isFullyOpened: Boolean
+        get() = !isFolded && lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN
+
     private fun onHingeAngle(angle: Float) {
         when (lastFoldUpdate) {
             FOLD_UPDATE_FINISH_FULL_OPEN -> {
-                if (FULLY_OPEN_DEGREES - angle > MOVEMENT_THRESHOLD_DEGREES) {
+                if (FULLY_OPEN_DEGREES - angle > START_CLOSING_THRESHOLD_DEGREES) {
                     lastFoldUpdate = FOLD_UPDATE_START_CLOSING
                     outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_START_CLOSING) }
                 }
             }
-            FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING -> {
+            FOLD_UPDATE_START_OPENING -> {
                 if (FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES) {
                     lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
                     outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
                 }
             }
+            FOLD_UPDATE_START_CLOSING -> {
+                if (FULLY_OPEN_DEGREES - angle < START_CLOSING_THRESHOLD_DEGREES) {
+                    lastFoldUpdate = FOLD_UPDATE_FINISH_FULL_OPEN
+                    outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }
+                }
+            }
         }
 
         outputListeners.forEach { it.onHingeAngleUpdate(angle) }
@@ -120,5 +129,5 @@
     }
 }
 
-private const val MOVEMENT_THRESHOLD_DEGREES = 10f
-private const val FULLY_OPEN_THRESHOLD_DEGREES = 10f
\ No newline at end of file
+private const val START_CLOSING_THRESHOLD_DEGREES = 95f
+private const val FULLY_OPEN_THRESHOLD_DEGREES = 15f
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index 4c6d241..11984b93 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -28,6 +28,8 @@
     fun start()
     fun stop()
 
+    val isFullyOpened: Boolean
+
     interface FoldUpdatesListener {
         fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float)
         fun onFoldUpdate(@FoldUpdate update: Int)
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
index 8549913..2520d35 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt
@@ -3,6 +3,12 @@
 import androidx.core.util.Consumer
 import com.android.systemui.statusbar.policy.CallbackController
 
+/**
+ * Emits device hinge angle values (angle between two integral parts of the device).
+ * The hinge angle could be from 0 to 360 degrees inclusive.
+ * For foldable devices usually 0 corresponds to fully closed (folded) state and
+ * 180 degrees corresponds to fully open (flat) state
+ */
 internal interface HingeAngleProvider : CallbackController<Consumer<Float>> {
     fun start()
     fun stop()
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
new file mode 100644
index 0000000..a42ebef
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt
@@ -0,0 +1,42 @@
+package com.android.systemui.unfold.updates.hinge
+
+import android.hardware.Sensor
+import android.hardware.SensorEvent
+import android.hardware.SensorEventListener
+import android.hardware.SensorManager
+import androidx.core.util.Consumer
+
+internal class HingeSensorAngleProvider(
+    private val sensorManager: SensorManager
+) : HingeAngleProvider {
+
+    private val sensorListener = HingeAngleSensorListener()
+    private val listeners: MutableList<Consumer<Float>> = arrayListOf()
+
+    override fun start() {
+        val sensor = sensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE)
+        sensorManager.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_FASTEST)
+    }
+
+    override fun stop() {
+        sensorManager.unregisterListener(sensorListener)
+    }
+
+    override fun removeCallback(listener: Consumer<Float>) {
+        listeners.remove(listener)
+    }
+
+    override fun addCallback(listener: Consumer<Float>) {
+        listeners.add(listener)
+    }
+
+    private inner class HingeAngleSensorListener : SensorEventListener {
+
+        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
+        }
+
+        override fun onSensorChanged(event: SensorEvent) {
+            listeners.forEach { it.accept(event.values[0]) }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index c093219..e7974bc 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -196,9 +196,11 @@
     @SysUISingleton
     @Provides
     static ThemeOverlayApplier provideThemeOverlayManager(Context context,
-            @Background Executor bgExecutor, OverlayManager overlayManager,
+            @Background Executor bgExecutor,
+            @Main Executor mainExecutor,
+            OverlayManager overlayManager,
             DumpManager dumpManager) {
-        return new ThemeOverlayApplier(overlayManager, bgExecutor,
+        return new ThemeOverlayApplier(overlayManager, bgExecutor, mainExecutor,
                 context.getString(R.string.launcher_overlayable_package),
                 context.getString(R.string.themepicker_overlayable_package), dumpManager);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index a85800b..7eedb3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -140,6 +140,10 @@
         mBatteryRemainingIcon = findViewById(R.id.batteryRemainingIcon);
 
         updateResources();
+        Configuration config = mContext.getResources().getConfiguration();
+        setDatePrivacyContainersWidth(config.orientation == Configuration.ORIENTATION_LANDSCAPE);
+        setSecurityHeaderContainerVisibility(
+                config.orientation == Configuration.ORIENTATION_LANDSCAPE);
 
         // QS will always show the estimate, and BatteryMeterView handles the case where
         // it's unavailable or charging
@@ -189,6 +193,8 @@
         super.onConfigurationChanged(newConfig);
         updateResources();
         setDatePrivacyContainersWidth(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE);
+        setSecurityHeaderContainerVisibility(
+                newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE);
     }
 
     @Override
@@ -209,6 +215,10 @@
         mPrivacyContainer.setLayoutParams(lp);
     }
 
+    private void setSecurityHeaderContainerVisibility(boolean landscape) {
+        mSecurityHeaderView.setVisibility(landscape ? VISIBLE : GONE);
+    }
+
     private void updateBatteryMode() {
         if (mConfigShowBatteryEstimate && !mHasCenterCutout) {
             mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
index c3b4fbe..fe0b970 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayApplier.java
@@ -132,14 +132,18 @@
     /* Target package for each overlay category. */
     private final Map<String, String> mCategoryToTargetPackage = new ArrayMap<>();
     private final OverlayManager mOverlayManager;
-    private final Executor mExecutor;
+    private final Executor mBgExecutor;
+    private final Executor mMainExecutor;
     private final String mLauncherPackage;
     private final String mThemePickerPackage;
 
-    public ThemeOverlayApplier(OverlayManager overlayManager, Executor executor,
+    public ThemeOverlayApplier(OverlayManager overlayManager,
+            Executor bgExecutor,
+            Executor mainExecutor,
             String launcherPackage, String themePickerPackage, DumpManager dumpManager) {
         mOverlayManager = overlayManager;
-        mExecutor = executor;
+        mBgExecutor = bgExecutor;
+        mMainExecutor = mainExecutor;
         mLauncherPackage = launcherPackage;
         mThemePickerPackage = themePickerPackage;
         mTargetPackageToCategories.put(ANDROID_PACKAGE, Sets.newHashSet(
@@ -170,12 +174,13 @@
      * Apply the set of overlay packages to the set of {@code UserHandle}s provided. Overlays that
      * affect sysui will also be applied to the system user.
      */
-    void applyCurrentUserOverlays(
+    public void applyCurrentUserOverlays(
             Map<String, OverlayIdentifier> categoryToPackage,
             FabricatedOverlay[] pendingCreation,
             int currentUser,
-            Set<UserHandle> managedProfiles) {
-        mExecutor.execute(() -> {
+            Set<UserHandle> managedProfiles,
+            Runnable onOverlaysApplied) {
+        mBgExecutor.execute(() -> {
 
             // Disable all overlays that have not been specified in the user setting.
             final Set<String> overlayCategoriesToDisable = new HashSet<>(THEME_CATEGORIES);
@@ -221,6 +226,7 @@
 
             try {
                 mOverlayManager.commit(transaction.build());
+                mMainExecutor.execute(onOverlaysApplied);
             } catch (SecurityException | IllegalStateException e) {
                 Log.e(TAG, "setEnabled failed", e);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 377a7e6..a5871f0 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -526,16 +526,16 @@
                     .map(key -> key + " -> " + categoryToPackage.get(key)).collect(
                             Collectors.joining(", ")));
         }
+        Runnable overlaysAppliedRunnable = () -> onOverlaysApplied();
         if (mNeedsOverlayCreation) {
             mNeedsOverlayCreation = false;
             mThemeManager.applyCurrentUserOverlays(categoryToPackage, new FabricatedOverlay[] {
                     mSecondaryOverlay, mNeutralOverlay
-            }, currentUser, managedProfiles);
+            }, currentUser, managedProfiles, overlaysAppliedRunnable);
         } else {
             mThemeManager.applyCurrentUserOverlays(categoryToPackage, null, currentUser,
-                    managedProfiles);
+                    managedProfiles, overlaysAppliedRunnable);
         }
-        onOverlaysApplied();
     }
 
     protected void onOverlaysApplied() {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 9283403..3c3cc64 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -58,6 +58,10 @@
         }
 
         override fun onTransitionStarted() {
+            // When unfolding the view is added earlier, add view for folding case
+            if (scrimView == null) {
+                addOverlayView()
+            }
         }
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
index 9c47f19..e6dc4db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
@@ -96,6 +96,8 @@
     DumpManager mDumpManager;
     @Mock
     OverlayManagerTransaction.Builder mTransactionBuilder;
+    @Mock
+    Runnable mOnOverlaysApplied;
 
     private ThemeOverlayApplier mManager;
     private boolean mGetOverlayInfoEnabled = true;
@@ -103,7 +105,8 @@
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mManager = new ThemeOverlayApplier(mOverlayManager, MoreExecutors.directExecutor(),
+        mManager = new ThemeOverlayApplier(mOverlayManager,
+                MoreExecutors.directExecutor(), MoreExecutors.directExecutor(),
                 LAUNCHER_PACKAGE, THEMEPICKER_PACKAGE, mDumpManager) {
             @Override
             protected OverlayManagerTransaction.Builder getTransactionBuilder() {
@@ -173,7 +176,7 @@
     @Test
     public void allCategoriesSpecified_allEnabledExclusively() {
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, mOnOverlaysApplied);
         verify(mOverlayManager).commit(any());
 
         for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
@@ -185,7 +188,7 @@
     @Test
     public void allCategoriesSpecified_sysuiCategoriesAlsoAppliedToSysuiUser() {
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, mOnOverlaysApplied);
 
         for (Map.Entry<String, OverlayIdentifier> entry : ALL_CATEGORIES_MAP.entrySet()) {
             if (SYSTEM_USER_CATEGORIES.contains(entry.getKey())) {
@@ -202,8 +205,9 @@
     public void allCategoriesSpecified_enabledForAllUserHandles() {
         Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                userHandles);
+                userHandles, mOnOverlaysApplied);
 
+        verify(mOnOverlaysApplied).run();
         for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
             verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
                     eq(TEST_USER.getIdentifier()));
@@ -219,7 +223,7 @@
 
         Set<UserHandle> userHandles = Sets.newHashSet(TEST_USER_HANDLES);
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, null, TEST_USER.getIdentifier(),
-                userHandles);
+                userHandles, mOnOverlaysApplied);
 
         for (OverlayIdentifier overlayPackage : ALL_CATEGORIES_MAP.values()) {
             verify(mTransactionBuilder, never()).setEnabled(eq(overlayPackage), eq(true),
@@ -233,7 +237,7 @@
                 mock(FabricatedOverlay.class)
         };
         mManager.applyCurrentUserOverlays(ALL_CATEGORIES_MAP, pendingCreation,
-                TEST_USER.getIdentifier(), TEST_USER_HANDLES);
+                TEST_USER.getIdentifier(), TEST_USER_HANDLES, mOnOverlaysApplied);
 
         for (FabricatedOverlay overlay : pendingCreation) {
             verify(mTransactionBuilder).registerFabricatedOverlay(eq(overlay));
@@ -247,7 +251,7 @@
         categoryToPackage.remove(OVERLAY_CATEGORY_ICON_ANDROID);
 
         mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, mOnOverlaysApplied);
 
         for (OverlayIdentifier overlayPackage : categoryToPackage.values()) {
             verify(mTransactionBuilder).setEnabled(eq(overlayPackage), eq(true),
@@ -264,7 +268,7 @@
     @Test
     public void zeroCategoriesSpecified_allDisabled() {
         mManager.applyCurrentUserOverlays(Maps.newArrayMap(), null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, mOnOverlaysApplied);
 
         for (String category : THEME_CATEGORIES) {
             verify(mTransactionBuilder).setEnabled(
@@ -279,7 +283,7 @@
         categoryToPackage.put("blah.category", new OverlayIdentifier("com.example.blah.category"));
 
         mManager.applyCurrentUserOverlays(categoryToPackage, null, TEST_USER.getIdentifier(),
-                TEST_USER_HANDLES);
+                TEST_USER_HANDLES, mOnOverlaysApplied);
 
         verify(mTransactionBuilder, never()).setEnabled(
                 eq(new OverlayIdentifier("com.example.blah.category")), eq(false),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index f6a54936..cd911cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -161,7 +161,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -183,7 +183,7 @@
         mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
         mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.BLACK),
                 null, null), WallpaperManager.FLAG_SYSTEM);
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -204,7 +204,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
@@ -240,7 +240,7 @@
                 .isFalse();
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -270,7 +270,7 @@
                 "android.theme.customization.color_both\":\"0")).isTrue();
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -300,7 +300,7 @@
                 "android.theme.customization.color_both\":\"1")).isTrue();
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -327,7 +327,7 @@
         assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
                 .isFalse();
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -354,7 +354,7 @@
         assertThat(updatedSetting.getValue().contains("android.theme.customization.color_index"))
                 .isFalse();
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -382,7 +382,7 @@
                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES), updatedSetting.capture());
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -411,14 +411,14 @@
 
 
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
     public void onProfileAdded_setsTheme() {
         mBroadcastReceiver.getValue().onReceive(null,
                 new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -428,7 +428,7 @@
         mBroadcastReceiver.getValue().onReceive(null,
                 new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -438,7 +438,7 @@
         mBroadcastReceiver.getValue().onReceive(null,
                 new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED));
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -450,7 +450,7 @@
                 Color.valueOf(Color.BLUE), null);
         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
 
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
 
         // Regression test: null events should not reset the internal state and allow colors to be
         // applied again.
@@ -458,11 +458,11 @@
         mBroadcastReceiver.getValue().onReceive(null, new Intent(Intent.ACTION_WALLPAPER_CHANGED));
         mColorsListener.getValue().onColorsChanged(null, WallpaperManager.FLAG_SYSTEM);
         verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
-                any());
+                any(), any());
         mColorsListener.getValue().onColorsChanged(new WallpaperColors(Color.valueOf(Color.GREEN),
                 null, null), WallpaperManager.FLAG_SYSTEM);
         verify(mThemeOverlayApplier, never()).applyCurrentUserOverlays(any(), any(), anyInt(),
-                any());
+                any(), any());
     }
 
     @Test
@@ -499,7 +499,7 @@
         verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
 
         // Colors were applied during controller initialization.
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
         clearInvocations(mThemeOverlayApplier);
     }
 
@@ -533,7 +533,7 @@
         verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
 
         // Colors were applied during controller initialization.
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
         clearInvocations(mThemeOverlayApplier);
 
         WallpaperColors mainColors = new WallpaperColors(Color.valueOf(Color.RED),
@@ -542,12 +542,12 @@
 
         // Defers event because we already have initial colors.
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
 
         // Then event happens after setup phase is over.
         when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true);
         mDeviceProvisionedListener.getValue().onUserSetupChanged();
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -568,11 +568,11 @@
                 Color.valueOf(Color.RED), null);
         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
 
         mWakefulnessLifecycle.dispatchFinishedGoingToSleep();
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -592,10 +592,10 @@
                 Color.valueOf(Color.RED), null);
         mColorsListener.getValue().onColorsChanged(mainColors, WallpaperManager.FLAG_SYSTEM);
         verify(mThemeOverlayApplier, never())
-                .applyCurrentUserOverlays(any(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
 
         mWakefulnessLifecycleObserver.getValue().onFinishedGoingToSleep();
-        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any());
+        verify(mThemeOverlayApplier).applyCurrentUserOverlays(any(), any(), anyInt(), any(), any());
     }
 
     @Test
@@ -614,7 +614,7 @@
                 ArgumentCaptor.forClass(Map.class);
 
         verify(mThemeOverlayApplier)
-                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any());
+                .applyCurrentUserOverlays(themeOverlays.capture(), any(), anyInt(), any(), any());
 
         // Assert that we received the colors that we were expecting
         assertThat(themeOverlays.getValue().get(OVERLAY_CATEGORY_SYSTEM_PALETTE))
diff --git a/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
index 5f9aaae..9801407 100644
--- a/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
+++ b/services/accessibility/java/com/android/server/accessibility/PolicyWarningUIController.java
@@ -40,6 +40,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -47,6 +48,7 @@
 import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.R;
+import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.ImageUtils;
@@ -71,6 +73,7 @@
     @VisibleForTesting
     protected static final String ACTION_DISMISS_NOTIFICATION =
             TAG + ".ACTION_DISMISS_NOTIFICATION";
+    private static final String EXTRA_TIME_FOR_LOGGING = "start_time_to_log_a11y_tool";
     private static final int SEND_NOTIFICATION_DELAY_HOURS = 24;
 
     /** Current enabled accessibility services. */
@@ -179,7 +182,8 @@
         intent.setPackage(context.getPackageName())
                 .setIdentifier(serviceComponentName.flattenToShortString())
                 .putExtra(Intent.EXTRA_COMPONENT_NAME, serviceComponentName)
-                .putExtra(Intent.EXTRA_USER_ID, userId);
+                .putExtra(Intent.EXTRA_USER_ID, userId)
+                .putExtra(Intent.EXTRA_TIME, SystemClock.elapsedRealtime());
         return intent;
     }
 
@@ -214,11 +218,24 @@
             if (TextUtils.isEmpty(action) || componentName == null) {
                 return;
             }
+            final long startTimeMills = intent.getLongExtra(Intent.EXTRA_TIME, 0);
+            final long durationMills =
+                    startTimeMills > 0 ? SystemClock.elapsedRealtime() - startTimeMills : 0;
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_ID, UserHandle.USER_SYSTEM);
             if (ACTION_SEND_NOTIFICATION.equals(action)) {
-                trySendNotification(userId, componentName);
+                if (trySendNotification(userId, componentName)) {
+                    AccessibilityStatsLogUtils.logNonA11yToolServiceWarningReported(
+                            componentName.getPackageName(),
+                            AccessibilityStatsLogUtils.ACCESSIBILITY_PRIVACY_WARNING_STATUS_SHOWN,
+                            durationMills);
+                }
             } else if (ACTION_A11Y_SETTINGS.equals(action)) {
-                launchSettings(userId, componentName);
+                if (tryLaunchSettings(userId, componentName)) {
+                    AccessibilityStatsLogUtils.logNonA11yToolServiceWarningReported(
+                            componentName.getPackageName(),
+                            AccessibilityStatsLogUtils.ACCESSIBILITY_PRIVACY_WARNING_STATUS_CLICKED,
+                            durationMills);
+                }
                 mNotificationManager.cancel(componentName.flattenToShortString(),
                         NOTE_A11Y_VIEW_AND_CONTROL_ACCESS);
                 onNotificationCanceled(userId, componentName);
@@ -240,12 +257,12 @@
             }
         }
 
-        private void trySendNotification(int userId, ComponentName componentName) {
+        private boolean trySendNotification(int userId, ComponentName componentName) {
             if (!AccessibilitySecurityPolicy.POLICY_WARNING_ENABLED) {
-                return;
+                return false;
             }
             if (userId != mCurrentUserId) {
-                return;
+                return false;
             }
 
             List<AccessibilityServiceInfo> enabledServiceInfos = getEnabledServiceInfos();
@@ -264,23 +281,27 @@
                                 android.R.dimen.app_icon_size);
                         sendNotification(userId, componentName, displayName,
                                 ImageUtils.buildScaledBitmap(drawable, size, size));
+                        return true;
                     }
                     break;
                 }
             }
+            return false;
         }
 
-        private void launchSettings(int userId, ComponentName componentName) {
+        private boolean tryLaunchSettings(int userId, ComponentName componentName) {
             if (userId != mCurrentUserId) {
-                return;
+                return false;
             }
             final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
             intent.putExtra(Intent.EXTRA_COMPONENT_NAME, componentName.flattenToShortString());
+            intent.putExtra(EXTRA_TIME_FOR_LOGGING, SystemClock.elapsedRealtime());
             final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(
                     mContext.getDisplayId()).toBundle();
             mContext.startActivityAsUser(intent, bundle, UserHandle.of(userId));
             mContext.getSystemService(StatusBarManager.class).collapsePanels();
+            return true;
         }
 
         protected void onNotificationCanceled(int userId, ComponentName componentName) {
diff --git a/services/core/java/com/android/server/audio/RotationHelper.java b/services/core/java/com/android/server/audio/RotationHelper.java
index ad72166..872b1fe 100644
--- a/services/core/java/com/android/server/audio/RotationHelper.java
+++ b/services/core/java/com/android/server/audio/RotationHelper.java
@@ -21,20 +21,26 @@
 import android.media.AudioSystem;
 import android.os.Handler;
 import android.util.Log;
+import android.view.Display;
 import android.view.Surface;
 import android.view.WindowManager;
 
 /**
  * Class to handle device rotation events for AudioService, and forward device rotation
- * to the audio HALs through AudioSystem.
+ * and current active ID to the audio HALs through AudioSystem.
  *
  * The role of this class is to monitor device orientation changes, and upon rotation,
  * verify the UI orientation. In case of a change, send the new orientation, in increments
  * of 90deg, through AudioSystem.
  *
+ * Another role of this class is to track current active display ID changes. In case of a
+ * change, send the new active display ID through AudioSystem.
+ *
  * Note that even though we're responding to device orientation events, we always
  * query the display rotation so audio stays in sync with video/dialogs. This is
  * done with .getDefaultDisplay().getRotation() from WINDOW_SERVICE.
+ *
+ * We also monitor current display ID and audio is able to know which display is active.
  */
 class RotationHelper {
 
@@ -43,7 +49,9 @@
     private static AudioDisplayListener sDisplayListener;
 
     private static final Object sRotationLock = new Object();
+    private static final Object sActiveDisplayLock = new Object();
     private static int sDeviceRotation = Surface.ROTATION_0; // R/W synchronized on sRotationLock
+    private static int sDisplayId = Display.DEFAULT_DISPLAY; // synchronized on sActiveDisplayLock
 
     private static Context sContext;
     private static Handler sHandler;
@@ -112,6 +120,18 @@
     }
 
     /**
+     * Query current display active id and publish the change if any.
+     */
+    static void updateActiveDisplayId(int displayId) {
+        synchronized (sActiveDisplayLock) {
+            if (displayId != Display.DEFAULT_DISPLAY && sDisplayId != displayId) {
+                sDisplayId = displayId;
+                AudioSystem.setParameters("active_displayId=" + sDisplayId);
+            }
+        }
+    }
+
+    /**
      * Uses android.hardware.display.DisplayManager.DisplayListener
      */
     final static class AudioDisplayListener implements DisplayManager.DisplayListener {
@@ -126,6 +146,7 @@
 
         @Override
         public void onDisplayChanged(int displayId) {
+            updateActiveDisplayId(displayId);
             updateOrientation();
         }
     }
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index 557fa89..df372b1 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -290,7 +290,8 @@
                 combined.getValues(augmentStart, augmentEnd, entry);
             }
 
-            final long rawBytes = entry.rxBytes + entry.txBytes;
+            final long rawBytes = (entry.rxBytes + entry.txBytes) == 0 ? 1 :
+                    (entry.rxBytes + entry.txBytes);
             final long rawRxBytes = entry.rxBytes == 0 ? 1 : entry.rxBytes;
             final long rawTxBytes = entry.txBytes == 0 ? 1 : entry.txBytes;
             final long targetBytes = augmentPlan.getDataUsageBytes();
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 83085cc..1ebb722 100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -114,6 +114,8 @@
     private final SparseBooleanArray mHdmiStateMap = new SparseBooleanArray();
     private final List<Message> mPendingHdmiDeviceEvents = new LinkedList<>();
 
+    private final List<Message> mPendingTvinputInfoEvents = new LinkedList<>();
+
     // Calls to mListener should happen here.
     private final Handler mHandler = new ListenerHandler();
 
@@ -229,7 +231,16 @@
                             connection.getInputStateLocked(), 0, inputId).sendToTarget();
                     }
                 }
-            }
+            } else {
+                Message msg = mHandler.obtainMessage(ListenerHandler.TVINPUT_INFO_ADDED,
+                    deviceId, cableConnectionStatus, connection);
+                for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext();) {
+                    if (it.next().arg1 == deviceId) {
+                    it.remove();
+                    }
+                }
+                mPendingTvinputInfoEvents.add(msg);
+           }
             ITvInputHardwareCallback callback = connection.getCallbackLocked();
             if (callback != null) {
                 try {
@@ -288,6 +299,8 @@
             }
             mHardwareInputIdMap.put(deviceId, info.getId());
             mInputMap.put(info.getId(), info);
+            processPendingTvInputInfoEventsLocked();
+            Slog.d(TAG,"deviceId ="+ deviceId+", tvinputinfo = "+info);
 
             // Process pending state changes
 
@@ -530,6 +543,20 @@
         }
     }
 
+
+    private void processPendingTvInputInfoEventsLocked() {
+        for (Iterator<Message> it = mPendingTvinputInfoEvents.iterator(); it.hasNext(); ) {
+            Message msg = it.next();
+            int deviceId =  msg.arg1;
+            String inputId = mHardwareInputIdMap.get(deviceId);
+            if (inputId != null) {
+                msg.sendToTarget();
+                it.remove();
+            }
+        }
+    }
+
+
     private void updateVolume() {
         mCurrentMaxIndex = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         mCurrentIndex = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
@@ -1182,6 +1209,7 @@
         private static final int HDMI_DEVICE_ADDED = 4;
         private static final int HDMI_DEVICE_REMOVED = 5;
         private static final int HDMI_DEVICE_UPDATED = 6;
+        private static final int TVINPUT_INFO_ADDED = 7;
 
         @Override
         public final void handleMessage(Message msg) {
@@ -1226,6 +1254,29 @@
                     }
                     break;
                 }
+                case TVINPUT_INFO_ADDED: {
+                    int deviceId = msg.arg1;
+                    int cableConnectionStatus = msg.arg2;
+                    Connection connection =(Connection)msg.obj;
+
+                    int previousConfigsLength = connection.getConfigsLengthLocked();
+                    int previousCableConnectionStatus = connection.getInputStateLocked();
+                    String inputId = mHardwareInputIdMap.get(deviceId);
+
+                    if (inputId != null) {
+                        if (connection.updateCableConnectionStatusLocked(cableConnectionStatus)) {
+                            if (previousCableConnectionStatus != connection.getInputStateLocked()) {
+                                mListener.onStateChanged(inputId, connection.getInputStateLocked());
+                            }
+                        } else {
+                            if ((previousConfigsLength == 0)
+                                    != (connection.getConfigsLengthLocked() == 0)) {
+                                mListener.onStateChanged(inputId, connection.getInputStateLocked());
+                            }
+                        }
+                    }
+                    break;
+                }
                 default: {
                     Slog.w(TAG, "Unhandled message: " + msg);
                     break;
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 0efc940..c5c0325 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -1380,8 +1380,8 @@
                     // At this point we have an externally controlled vibration playing already.
                     // Since the interface defines that only one externally controlled vibration can
                     // play at a time, we need to first mute the ongoing vibration and then return
-                    // a scale from this function for the new one. Ee can be assured that the
-                    // ongoing it will be muted in favor of the new vibration.
+                    // a scale from this function for the new one, so we can be assured that the
+                    // ongoing will be muted in favor of the new vibration.
                     //
                     // Note that this doesn't support multiple concurrent external controls, as we
                     // would need to mute the old one still if it came from a different controller.
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/OWNERS b/services/tests/mockingservicestests/src/com/android/server/tare/OWNERS
new file mode 100644
index 0000000..217a5ed
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/OWNERS
@@ -0,0 +1 @@
+include /apex/jobscheduler/service/java/com/android/server/tare/OWNERS
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
index 8f9eb22..2c5159a 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java
@@ -35,7 +35,6 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
@@ -1016,7 +1015,7 @@
         assertEquals(IExternalVibratorService.SCALE_NONE, firstScale);
         assertEquals(IExternalVibratorService.SCALE_NONE, secondScale);
         verify(firstController).mute();
-        verifyNoMoreInteractions(secondController);
+        verify(secondController, never()).mute();
         // Set external control called only once.
         assertEquals(Arrays.asList(true), mVibratorProviders.get(1).getExternalControlStates());
     }