Merge "Disable tests flaky on flame" into sc-dev
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index ed55f00..3bbc945 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -64,7 +64,7 @@
     public void onStart() {
         publishBinderService(Context.APP_SEARCH_SERVICE, new Stub());
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
-        mImplInstanceManager = new ImplInstanceManager(getContext());
+        mImplInstanceManager = ImplInstanceManager.getInstance(getContext());
     }
 
     private class Stub extends IAppSearchManager.Stub {
@@ -102,7 +102,8 @@
                     }
                     schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
                 }
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 impl.setSchema(
                         packageName,
                         databaseName,
@@ -133,7 +134,8 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 List<AppSearchSchema> schemas = impl.getSchema(packageName, databaseName);
                 List<Bundle> schemaBundles = new ArrayList<>(schemas.size());
                 for (int i = 0; i < schemas.size(); i++) {
@@ -166,7 +168,8 @@
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 for (int i = 0; i < documentBundles.size(); i++) {
                     GenericDocument document = new GenericDocument(documentBundles.get(i));
                     try {
@@ -207,12 +210,18 @@
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
-                        GenericDocument document = impl.getDocument(packageName, databaseName,
-                                namespace, uri, typePropertyPaths);
+                        GenericDocument document =
+                                impl.getDocument(
+                                        packageName,
+                                        databaseName,
+                                        namespace,
+                                        uri,
+                                        typePropertyPaths);
                         resultBuilder.setSuccess(uri, document.getBundle());
                     } catch (Throwable t) {
                         resultBuilder.setResult(uri, throwableToFailedResult(t));
@@ -245,7 +254,8 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 SearchResultPage searchResultPage =
                         impl.query(
                                 packageName,
@@ -278,12 +288,14 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
-                SearchResultPage searchResultPage = impl.globalQuery(
-                        queryExpression,
-                        new SearchSpec(searchSpecBundle),
-                        packageName,
-                        callingUid);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                SearchResultPage searchResultPage =
+                        impl.globalQuery(
+                                queryExpression,
+                                new SearchSpec(searchSpecBundle),
+                                packageName,
+                                callingUid);
                 invokeCallbackOnResult(
                         callback,
                         AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
@@ -306,7 +318,8 @@
             // TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally
             // opened it
             try {
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
                 invokeCallbackOnResult(
                         callback,
@@ -324,7 +337,8 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 impl.invalidateNextPageToken(nextPageToken);
             } catch (Throwable t) {
                 Log.e(TAG, "Unable to invalidate the query page token", t);
@@ -350,15 +364,11 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
-                impl.reportUsage(
-                        packageName,
-                        databaseName,
-                        namespace,
-                        uri,
-                        usageTimeMillis);
-                invokeCallbackOnResult(callback,
-                        AppSearchResult.newSuccessfulResult(/*result=*/ null));
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+                impl.reportUsage(packageName, databaseName, namespace, uri, usageTimeMillis);
+                invokeCallbackOnResult(
+                        callback, AppSearchResult.newSuccessfulResult(/*result=*/ null));
             } catch (Throwable t) {
                 invokeCallbackOnError(callback, t);
             } finally {
@@ -385,7 +395,8 @@
                 verifyCallingPackage(callingUid, packageName);
                 AppSearchBatchResult.Builder<String, Void> resultBuilder =
                         new AppSearchBatchResult.Builder<>();
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 for (int i = 0; i < uris.size(); i++) {
                     String uri = uris.get(i);
                     try {
@@ -421,7 +432,8 @@
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
                 verifyCallingPackage(callingUid, packageName);
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 impl.removeByQuery(
                         packageName,
                         databaseName,
@@ -441,7 +453,8 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+                AppSearchImpl impl =
+                        mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 impl.persistToDisk();
             } catch (Throwable t) {
                 Log.e(TAG, "Unable to persist the data to disk", t);
@@ -457,7 +470,7 @@
             int callingUserId = handleIncomingUser(userId, callingUid);
             final long callingIdentity = Binder.clearCallingIdentity();
             try {
-                mImplInstanceManager.getInstance(callingUserId);
+                mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
                 invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
             } catch (Throwable t) {
                 invokeCallbackOnError(callback, t);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index fe3c2e1..97b1a8c 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -41,14 +41,33 @@
 public final class ImplInstanceManager {
     private static final String APP_SEARCH_DIR = "appSearch";
 
-    private static final SparseArray<AppSearchImpl> sInstances = new SparseArray<>();
+    private static ImplInstanceManager sImplInstanceManager;
 
-    private final Context mContext;
+    private final SparseArray<AppSearchImpl> mInstances = new SparseArray<>();
     private final String mGlobalQuerierPackage;
 
-    public ImplInstanceManager(@NonNull Context context) {
-        mContext = context;
-        mGlobalQuerierPackage = getGlobalAppSearchDataQuerierPackageName(mContext);
+    private ImplInstanceManager(@NonNull String globalQuerierPackage) {
+        mGlobalQuerierPackage = globalQuerierPackage;
+    }
+
+    /**
+     * Gets an instance of ImplInstanceManager to be used.
+     *
+     * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the
+     * existing instance will be returned.
+     */
+    @NonNull
+    public static ImplInstanceManager getInstance(@NonNull Context context) {
+        if (sImplInstanceManager == null) {
+            synchronized (ImplInstanceManager.class) {
+                if (sImplInstanceManager == null) {
+                    sImplInstanceManager =
+                            new ImplInstanceManager(
+                                    getGlobalAppSearchDataQuerierPackageName(context));
+                }
+            }
+        }
+        return sImplInstanceManager;
     }
 
     /**
@@ -57,30 +76,30 @@
      * <p>If no AppSearchImpl instance exists for this user, Icing will be initialized and one will
      * be created.
      *
+     * @param context The context
      * @param userId The multi-user userId of the device user calling AppSearch
      * @return An initialized {@link AppSearchImpl} for this user
      */
     @NonNull
-    public AppSearchImpl getInstance(@UserIdInt int userId)
+    public AppSearchImpl getAppSearchImpl(@NonNull Context context, @UserIdInt int userId)
             throws AppSearchException {
-        AppSearchImpl instance = sInstances.get(userId);
+        AppSearchImpl instance = mInstances.get(userId);
         if (instance == null) {
             synchronized (ImplInstanceManager.class) {
-                instance = sInstances.get(userId);
+                instance = mInstances.get(userId);
                 if (instance == null) {
-                    instance = createImpl(userId);
-                    sInstances.put(userId, instance);
+                    instance = createImpl(context, userId);
+                    mInstances.put(userId, instance);
                 }
             }
         }
         return instance;
     }
 
-    private AppSearchImpl createImpl(@UserIdInt int userId)
+    private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
             throws AppSearchException {
-        File appSearchDir = getAppSearchDir(mContext, userId);
-        return AppSearchImpl.create(
-                appSearchDir, mContext, userId, mGlobalQuerierPackage);
+        File appSearchDir = getAppSearchDir(context, userId);
+        return AppSearchImpl.create(appSearchDir, context, userId, mGlobalQuerierPackage);
     }
 
     private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
@@ -96,7 +115,8 @@
      *
      * @param context Context of the system service.
      */
-    private static String getGlobalAppSearchDataQuerierPackageName(Context context) {
+    @NonNull
+    private static String getGlobalAppSearchDataQuerierPackageName(@NonNull Context context) {
         String globalAppSearchDataQuerierPackage =
                 context.getString(R.string.config_globalAppSearchDataQuerierPackage);
         try {
diff --git a/core/api/current.txt b/core/api/current.txt
index ec712d8..3dd1f59 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -46182,6 +46182,7 @@
     method @NonNull public android.graphics.Rect getBoundingRectRight();
     method @NonNull public android.graphics.Rect getBoundingRectTop();
     method @NonNull public java.util.List<android.graphics.Rect> getBoundingRects();
+    method @Nullable public android.graphics.Path getCutoutPath();
     method public int getSafeInsetBottom();
     method public int getSafeInsetLeft();
     method public int getSafeInsetRight();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index c237d7d..ea7dc03 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -684,6 +684,7 @@
     method public void holdLock(android.os.IBinder, int);
     field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
     field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
+    field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
     field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
     field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
     field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 68d3a92..49f508d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5255,6 +5255,7 @@
                 // We still want a time to be set but gone, such that we can show and hide it
                 // on demand in case it's a child notification without anything in the header
                 contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
+                setTextViewColorSecondary(contentView, R.id.time, p);
             }
         }
 
diff --git a/core/java/android/app/people/ConversationChannel.aidl b/core/java/android/app/people/ConversationChannel.aidl
new file mode 100644
index 0000000..78df2f1
--- /dev/null
+++ b/core/java/android/app/people/ConversationChannel.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2021, 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 android.app.people;
+
+parcelable ConversationChannel;
\ No newline at end of file
diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl
index 0d12ed0..ebe9f60 100644
--- a/core/java/android/app/people/IPeopleManager.aidl
+++ b/core/java/android/app/people/IPeopleManager.aidl
@@ -17,6 +17,7 @@
 package android.app.people;
 
 import android.app.people.ConversationStatus;
+import android.app.people.ConversationChannel;
 import android.content.pm.ParceledListSlice;
 import android.net.Uri;
 import android.os.IBinder;
@@ -26,6 +27,13 @@
  * {@hide}
  */
 interface IPeopleManager {
+
+    /**
+    * Returns the specified conversation from the conversations list. If the conversation can't be
+    * found, returns null.
+    */
+    ConversationChannel getConversation(in String packageName, int userId, in String shortcutId);
+
     /**
      * Returns the recent conversations. The conversations that have customized notification
      * settings are excluded from the returned list.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 68792b2..9ae9c25 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3385,6 +3385,7 @@
      * {@link #hasSystemFeature}: This device supports HDMI-CEC.
      * @hide
      */
+    @TestApi
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
 
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index fe8bf9d..e6c0f6a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6265,6 +6265,55 @@
         }
 
         /**
+         * Returns whether this {@code SigningDetails} has a signer in common with the provided
+         * {@code otherDetails} with the specified {@code flags} capabilities provided by this
+         * signer.
+         *
+         * <p>Note this method allows for the signing lineage to diverge, so this should only be
+         * used for instances where the only requirement is a common signer in the lineage with
+         * the specified capabilities. If the current signer of this instance is an ancestor of
+         * {@code otherDetails} then {@code true} is immediately returned since the current signer
+         * has all capabilities granted.
+         */
+        public boolean hasCommonSignerWithCapability(SigningDetails otherDetails,
+                @CertCapabilities int flags) {
+            if (this == UNKNOWN || otherDetails == UNKNOWN) {
+                return false;
+            }
+            // If either is signed with more than one signer then both must be signed by the same
+            // signers to consider the capabilities granted.
+            if (signatures.length > 1 || otherDetails.signatures.length > 1) {
+                return signaturesMatchExactly(otherDetails);
+            }
+            // The Signature class does not use the granted capabilities in the hashCode
+            // computation, so a Set can be used to check for a common signer.
+            Set<Signature> otherSignatures = new ArraySet<>();
+            if (otherDetails.hasPastSigningCertificates()) {
+                otherSignatures.addAll(Arrays.asList(otherDetails.pastSigningCertificates));
+            } else {
+                otherSignatures.addAll(Arrays.asList(otherDetails.signatures));
+            }
+            // If the current signer of this instance is an ancestor of the other than return true
+            // since all capabilities are granted to the current signer.
+            if (otherSignatures.contains(signatures[0])) {
+                return true;
+            }
+            if (hasPastSigningCertificates()) {
+                // Since the current signer was checked above and the last signature in the
+                // pastSigningCertificates is the current signer skip checking the last element.
+                for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+                    if (otherSignatures.contains(pastSigningCertificates[i])) {
+                        // If the caller specified multiple capabilities ensure all are set.
+                        if ((pastSigningCertificates[i].getFlags() & flags) == flags) {
+                            return true;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+
+        /**
          * Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or
          * not this one grants it the provided capability, represented by the {@code flags}
          * parameter.  In the event of signing certificate rotation, a package may still interact
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index b1b2925..8b6082b3 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -28,7 +28,10 @@
 import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
 import static android.app.AppOpsManager.opToPermission;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED;
+import static android.media.AudioSystem.MODE_IN_COMMUNICATION;
+import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
 
+import android.Manifest;
 import android.annotation.NonNull;
 import android.app.AppOpsManager;
 import android.content.ComponentName;
@@ -41,12 +44,14 @@
 import android.content.pm.ResolveInfo;
 import android.icu.text.ListFormatter;
 import android.location.LocationManager;
+import android.media.AudioManager;
 import android.os.Process;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.speech.RecognitionService;
 import android.speech.RecognizerIntent;
+import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.view.inputmethod.InputMethodInfo;
@@ -75,6 +80,9 @@
     private static final String PROPERTY_LOCATION_INDICATORS_ENABLED =
             "location_indicators_enabled";
 
+    /** Whether to show the Permissions Hub.  */
+    private static final String PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled";
+
     /** How long after an access to show it as "recent" */
     private static final String RECENT_ACCESS_TIME_MS = "recent_acccess_time_ms";
 
@@ -84,17 +92,25 @@
     /** The name of the expected voice IME subtype */
     private static final String VOICE_IME_SUBTYPE = "voice";
 
+    private static final String SYSTEM_PKG = "android";
+
     private static final long DEFAULT_RUNNING_TIME_MS = 5000L;
     private static final long DEFAULT_RECENT_TIME_MS = 30000L;
 
+    private static boolean shouldShowPermissionsHub() {
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                PROPERTY_PERMISSIONS_HUB_2_ENABLED, false);
+    }
+
     private static boolean shouldShowIndicators() {
         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                PROPERTY_CAMERA_MIC_ICONS_ENABLED, true);
+                PROPERTY_CAMERA_MIC_ICONS_ENABLED, true) || shouldShowPermissionsHub();
     }
 
     private static boolean shouldShowLocationIndicator() {
         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
-                PROPERTY_LOCATION_INDICATORS_ENABLED, false);
+                PROPERTY_LOCATION_INDICATORS_ENABLED, false)
+                || shouldShowPermissionsHub();
     }
 
     private static long getRecentThreshold(Long now) {
@@ -113,7 +129,7 @@
     );
 
     private static final List<String> MIC_OPS = List.of(
-            OPSTR_PHONE_CALL_CAMERA,
+            OPSTR_PHONE_CALL_MICROPHONE,
             OPSTR_RECORD_AUDIO
     );
 
@@ -163,6 +179,13 @@
         return mUserContexts.get(user);
     }
 
+    // TODO ntmyren: Replace this with better check if this moves beyond teamfood
+    private boolean isAppPredictor(String packageName, UserHandle user) {
+        return shouldShowPermissionsHub() && getUserContext(user).getPackageManager()
+                .checkPermission(Manifest.permission.MANAGE_APP_PREDICTIONS, packageName)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
     /**
      * @see PermissionManager.getIndicatorAppOpUsageData
      */
@@ -186,7 +209,28 @@
         Map<PackageAttribution, CharSequence> packagesWithAttributionLabels =
                 getTrustedAttributions(rawUsages.get(MICROPHONE), proxyChains);
 
-        List<String> usedPermGroups = new ArrayList<>(rawUsages.keySet());
+        ArrayList<String> usedPermGroups = new ArrayList<>(rawUsages.keySet());
+
+        // If we have a phone call, and a carrier privileged app using microphone, hide the
+        // phone call.
+        AudioManager audioManager = mContext.getSystemService(AudioManager.class);
+        boolean hasPhoneCall = usedPermGroups.contains(OPSTR_PHONE_CALL_CAMERA)
+                || usedPermGroups.contains(OPSTR_PHONE_CALL_MICROPHONE);
+        if (hasPhoneCall && usedPermGroups.contains(MICROPHONE) && audioManager.getMode()
+                == MODE_IN_COMMUNICATION) {
+            TelephonyManager telephonyManager =
+                    mContext.getSystemService(TelephonyManager.class);
+            List<OpUsage> permUsages = rawUsages.get(MICROPHONE);
+            for (int usageNum = 0; usageNum < permUsages.size(); usageNum++) {
+                if (telephonyManager.checkCarrierPrivilegesForPackage(
+                        permUsages.get(usageNum).packageName)
+                        == CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+                    usedPermGroups.remove(OPSTR_PHONE_CALL_CAMERA);
+                    usedPermGroups.remove(OPSTR_PHONE_CALL_MICROPHONE);
+                }
+            }
+        }
+
         for (int permGroupNum = 0; permGroupNum < usedPermGroups.size(); permGroupNum++) {
             boolean isPhone = false;
             String permGroup = usedPermGroups.get(permGroupNum);
@@ -269,8 +313,11 @@
                     if (lastAccessTime < recentThreshold && !attrOpEntry.isRunning()) {
                         continue;
                     }
-                    if (!isUserSensitive(packageName, user, op)
-                            && !isLocationProvider(packageName, user)) {
+
+                    if (packageName.equals(SYSTEM_PKG)
+                            || (!isUserSensitive(packageName, user, op)
+                            && !isLocationProvider(packageName, user)
+                            && !isAppPredictor(packageName, user))) {
                         continue;
                     }
 
diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java
index a44ed59..698cb77 100644
--- a/core/java/android/util/RotationUtils.java
+++ b/core/java/android/util/RotationUtils.java
@@ -21,7 +21,9 @@
 import static android.view.Surface.ROTATION_270;
 import static android.view.Surface.ROTATION_90;
 
+import android.annotation.Dimension;
 import android.graphics.Insets;
+import android.graphics.Matrix;
 import android.view.Surface.Rotation;
 
 /**
@@ -69,4 +71,34 @@
         }
         return rotated;
     }
+
+    /**
+     * Sets a matrix such that given a rotation, it transforms physical display
+     * coordinates to that rotation's logical coordinates.
+     *
+     * @param rotation the rotation to which the matrix should transform
+     * @param out the matrix to be set
+     */
+    public static void transformPhysicalToLogicalCoordinates(@Rotation int rotation,
+            @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
+        switch (rotation) {
+            case ROTATION_0:
+                out.reset();
+                break;
+            case ROTATION_90:
+                out.setRotate(270);
+                out.postTranslate(0, physicalWidth);
+                break;
+            case ROTATION_180:
+                out.setRotate(180);
+                out.postTranslate(physicalWidth, physicalHeight);
+                break;
+            case ROTATION_270:
+                out.setRotate(90);
+                out.postTranslate(physicalHeight, 0);
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown rotation: " + rotation);
+        }
+    }
 }
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 525ac53..e1a4402 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -23,6 +23,7 @@
 import static android.view.DisplayCutoutProto.BOUND_RIGHT;
 import static android.view.DisplayCutoutProto.BOUND_TOP;
 import static android.view.DisplayCutoutProto.INSETS;
+import static android.view.Surface.ROTATION_0;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
 
@@ -31,13 +32,16 @@
 import android.annotation.Nullable;
 import android.content.res.Resources;
 import android.graphics.Insets;
+import android.graphics.Matrix;
 import android.graphics.Path;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.Pair;
+import android.util.RotationUtils;
 import android.util.proto.ProtoOutputStream;
+import android.view.Surface.Rotation;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -69,6 +73,9 @@
             "com.android.internal.display_cutout_emulation";
 
     private static final Rect ZERO_RECT = new Rect();
+    private static final CutoutPathParserInfo EMPTY_PARSER_INFO = new CutoutPathParserInfo(
+            0 /* displayWidth */, 0 /* displayHeight */, 0f /* density */, "" /* cutoutSpec */,
+            0 /* rotation */, 0f /* scale */);
 
     /**
      * An instance where {@link #isEmpty()} returns {@code true}.
@@ -76,7 +83,7 @@
      * @hide
      */
     public static final DisplayCutout NO_CUTOUT = new DisplayCutout(
-            ZERO_RECT, Insets.NONE, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT,
+            ZERO_RECT, Insets.NONE, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT, EMPTY_PARSER_INFO,
             false /* copyArguments */);
 
 
@@ -96,11 +103,15 @@
     @GuardedBy("CACHE_LOCK")
     private static Insets sCachedWaterfallInsets;
 
+    @GuardedBy("CACHE_LOCK")
+    private static CutoutPathParserInfo sCachedCutoutPathParserInfo;
+    @GuardedBy("CACHE_LOCK")
+    private static Path sCachedCutoutPath;
+
     private final Rect mSafeInsets;
     @NonNull
     private final Insets mWaterfallInsets;
 
-
     /**
      * The bound is at the left of the screen.
      * @hide
@@ -210,6 +221,7 @@
             }
             return result;
         }
+
         @Override
         public boolean equals(@Nullable Object o) {
             if (o == this) {
@@ -232,6 +244,106 @@
     private final Bounds mBounds;
 
     /**
+     * Stores all the needed info to create the cutout paths.
+     *
+     * @hide
+     */
+    public static class CutoutPathParserInfo {
+        private final int mDisplayWidth;
+        private final int mDisplayHeight;
+        private final float mDensity;
+        private final String mCutoutSpec;
+        private final @Rotation int mRotation;
+        private final float mScale;
+
+        public CutoutPathParserInfo(int displayWidth, int displayHeight, float density,
+                String cutoutSpec, @Rotation int rotation, float scale) {
+            mDisplayWidth = displayWidth;
+            mDisplayHeight = displayHeight;
+            mDensity = density;
+            mCutoutSpec = cutoutSpec == null ? "" : cutoutSpec;
+            mRotation = rotation;
+            mScale = scale;
+        }
+
+        public CutoutPathParserInfo(CutoutPathParserInfo cutoutPathParserInfo) {
+            mDisplayWidth = cutoutPathParserInfo.mDisplayWidth;
+            mDisplayHeight = cutoutPathParserInfo.mDisplayHeight;
+            mDensity = cutoutPathParserInfo.mDensity;
+            mCutoutSpec = cutoutPathParserInfo.mCutoutSpec;
+            mRotation = cutoutPathParserInfo.mRotation;
+            mScale = cutoutPathParserInfo.mScale;
+        }
+
+        public int getDisplayWidth() {
+            return mDisplayWidth;
+        }
+
+        public int getDisplayHeight() {
+            return mDisplayHeight;
+        }
+
+        public float getDensity() {
+            return mDensity;
+        }
+
+        public @NonNull String getCutoutSpec() {
+            return mCutoutSpec;
+        }
+
+        public int getRotation() {
+            return mRotation;
+        }
+
+        public float getScale() {
+            return mScale;
+        }
+
+        private boolean hasCutout() {
+            return !mCutoutSpec.isEmpty();
+        }
+
+        @Override
+        public int hashCode() {
+            int result = 0;
+            result = result * 48271 + Integer.hashCode(mDisplayWidth);
+            result = result * 48271 + Integer.hashCode(mDisplayHeight);
+            result = result * 48271 + Float.hashCode(mDensity);
+            result = result * 48271 + mCutoutSpec.hashCode();
+            result = result * 48271 + Integer.hashCode(mRotation);
+            result = result * 48271 + Float.hashCode(mScale);
+            return result;
+        }
+
+        @Override
+        public boolean equals(@Nullable Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (o instanceof CutoutPathParserInfo) {
+                CutoutPathParserInfo c = (CutoutPathParserInfo) o;
+                return mDisplayWidth == c.mDisplayWidth && mDisplayHeight == c.mDisplayHeight
+                        && mDensity == c.mDensity && mCutoutSpec.equals(c.mCutoutSpec)
+                        && mRotation == c.mRotation && mScale == c.mScale;
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "CutoutPathParserInfo{displayWidth=" + mDisplayWidth
+                    + " displayHeight=" + mDisplayHeight
+                    + " density={" + mDensity + "}"
+                    + " cutoutSpec={" + mCutoutSpec + "}"
+                    + " rotation={" + mRotation + "}"
+                    + " scale={" + mScale + "}"
+                    + "}";
+        }
+    }
+
+    private final @NonNull CutoutPathParserInfo mCutoutPathParserInfo;
+
+    /**
      * Creates a DisplayCutout instance.
      *
      * <p>Note that this is only useful for tests. For production code, developers should always
@@ -251,7 +363,8 @@
     // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
     public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft,
             @Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom) {
-        this(safeInsets.toRect(), Insets.NONE, boundLeft, boundTop, boundRight, boundBottom, true);
+        this(safeInsets.toRect(), Insets.NONE, boundLeft, boundTop, boundRight, boundBottom, null,
+                true);
     }
 
     /**
@@ -276,7 +389,7 @@
             @Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom,
             @NonNull Insets waterfallInsets) {
         this(safeInsets.toRect(), waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
-                true);
+                null, true);
     }
 
     /**
@@ -294,7 +407,7 @@
     // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
     @Deprecated
     public DisplayCutout(@Nullable Rect safeInsets, @Nullable List<Rect> boundingRects) {
-        this(safeInsets, Insets.NONE, extractBoundsFromList(safeInsets, boundingRects),
+        this(safeInsets, Insets.NONE, extractBoundsFromList(safeInsets, boundingRects), null,
                 true /* copyArguments */);
     }
 
@@ -303,28 +416,42 @@
      *
      * @param safeInsets the insets from each edge which avoid the display cutout as returned by
      *                   {@link #getSafeInsetTop()} etc.
+     * @param waterfallInsets the insets for the curved areas in waterfall display.
+     * @param boundLeft the left bounding rect of the display cutout in pixels. If null is passed,
+     *                  it's treated as an empty rectangle (0,0)-(0,0).
+     * @param boundTop the top bounding rect of the display cutout in pixels.  If null is passed,
+     *                 it's treated as an empty rectangle (0,0)-(0,0).
+     * @param boundRight the right bounding rect of the display cutout in pixels.  If null is
+     *                   passed, it's treated as an empty rectangle (0,0)-(0,0).
+     * @param boundBottom the bottom bounding rect of the display cutout in pixels.  If null is
+     *                    passed, it's treated as an empty rectangle (0,0)-(0,0).
+     * @param info the cutout path parser info.
      * @param copyArguments if true, create a copy of the arguments. If false, the passed arguments
      *                      are not copied and MUST remain unchanged forever.
      */
-    private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect boundLeft,
-                        Rect boundTop, Rect boundRight, Rect boundBottom, boolean copyArguments) {
+    private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect boundLeft, Rect boundTop,
+            Rect boundRight, Rect boundBottom, CutoutPathParserInfo info,
+            boolean copyArguments) {
         mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
         mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
         mBounds = new Bounds(boundLeft, boundTop, boundRight, boundBottom, copyArguments);
+        mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
     }
 
     private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect[] bounds,
-                        boolean copyArguments) {
+            CutoutPathParserInfo info, boolean copyArguments) {
         mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
         mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
         mBounds = new Bounds(bounds, copyArguments);
+        mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
     }
 
-    private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds) {
+    private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds,
+            CutoutPathParserInfo info) {
         mSafeInsets = safeInsets;
         mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
         mBounds = bounds;
-
+        mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
     }
 
     private static Rect getCopyOrRef(Rect r, boolean copyArguments) {
@@ -534,10 +661,70 @@
         return mBounds.getRect(BOUNDS_POSITION_BOTTOM);
     }
 
+    /**
+     * Returns a {@link Path} that contains the cutout paths of all sides on the display.
+     *
+     * To get a cutout path for one specific side, apps can intersect the {@link Path} with the
+     * {@link Rect} obtained from {@link #getBoundingRectLeft()}, {@link #getBoundingRectTop()},
+     * {@link #getBoundingRectRight()} or {@link #getBoundingRectBottom()}.
+     *
+     * @return a {@link Path} contains all the cutout paths based on display coordinate. Returns
+     * null if there is no cutout on the display.
+     */
+    public @Nullable Path getCutoutPath() {
+        if (!mCutoutPathParserInfo.hasCutout()) {
+            return null;
+        }
+        synchronized (CACHE_LOCK) {
+            if (mCutoutPathParserInfo.equals(sCachedCutoutPathParserInfo)) {
+                return sCachedCutoutPath;
+            }
+        }
+        final CutoutSpecification cutoutSpec = new CutoutSpecification.Parser(
+                mCutoutPathParserInfo.getDensity(), mCutoutPathParserInfo.getDisplayWidth(),
+                mCutoutPathParserInfo.getDisplayHeight())
+                .parse(mCutoutPathParserInfo.getCutoutSpec());
+
+        final Path cutoutPath = cutoutSpec.getPath();
+        if (cutoutPath == null || cutoutPath.isEmpty()) {
+            return null;
+        }
+        final Matrix matrix = new Matrix();
+        if (mCutoutPathParserInfo.getRotation() != ROTATION_0) {
+            RotationUtils.transformPhysicalToLogicalCoordinates(
+                    mCutoutPathParserInfo.getRotation(),
+                    mCutoutPathParserInfo.getDisplayWidth(),
+                    mCutoutPathParserInfo.getDisplayHeight(),
+                    matrix
+            );
+        }
+        matrix.postScale(mCutoutPathParserInfo.getScale(), mCutoutPathParserInfo.getScale());
+        cutoutPath.transform(matrix);
+
+        synchronized (CACHE_LOCK) {
+            sCachedCutoutPathParserInfo = new CutoutPathParserInfo(mCutoutPathParserInfo);
+            sCachedCutoutPath = cutoutPath;
+        }
+        return cutoutPath;
+    }
+
+    /**
+     * @return the {@link CutoutPathParserInfo};
+     *
+     * @hide
+     */
+    public CutoutPathParserInfo getCutoutPathParserInfo() {
+        return mCutoutPathParserInfo;
+    }
+
     @Override
     public int hashCode() {
-        return (mSafeInsets.hashCode() * 48271 + mBounds.hashCode()) * 48271
-                + mWaterfallInsets.hashCode();
+        int result = 0;
+        result = 48271 * result + mSafeInsets.hashCode();
+        result = 48271 * result + mBounds.hashCode();
+        result = 48271 * result + mWaterfallInsets.hashCode();
+        result = 48271 * result + mCutoutPathParserInfo.hashCode();
+        return result;
     }
 
     @Override
@@ -548,7 +735,8 @@
         if (o instanceof DisplayCutout) {
             DisplayCutout c = (DisplayCutout) o;
             return mSafeInsets.equals(c.mSafeInsets) && mBounds.equals(c.mBounds)
-                    && mWaterfallInsets.equals(c.mWaterfallInsets);
+                    && mWaterfallInsets.equals(c.mWaterfallInsets)
+                    && mCutoutPathParserInfo.equals(c.mCutoutPathParserInfo);
         }
         return false;
     }
@@ -558,6 +746,7 @@
         return "DisplayCutout{insets=" + mSafeInsets
                 + " waterfall=" + mWaterfallInsets
                 + " boundingRect={" + mBounds + "}"
+                + " cutoutPathParserInfo={" + mCutoutPathParserInfo + "}"
                 + "}";
     }
 
@@ -607,7 +796,7 @@
         }
 
         return new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds,
-                false /* copyArguments */);
+                mCutoutPathParserInfo, false /* copyArguments */);
     }
 
     private Rect insetInsets(int insetLeft, int insetTop, int insetRight, int insetBottom,
@@ -638,7 +827,8 @@
      * @hide
      */
     public DisplayCutout replaceSafeInsets(Rect safeInsets) {
-        return new DisplayCutout(new Rect(safeInsets), mWaterfallInsets, mBounds);
+        return new DisplayCutout(new Rect(safeInsets), mWaterfallInsets, mBounds,
+                mCutoutPathParserInfo);
     }
 
     private static int atLeastZero(int value) {
@@ -658,16 +848,18 @@
         for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) {
             bounds[i] = (pos == i) ? new Rect(left, top, right, bottom) : new Rect();
         }
-        return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, false /* copyArguments */);
+        return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, null, false /* copyArguments */);
     }
 
     /**
-     * Creates an instance from a bounding and waterfall insets.
+     * Creates an instance from bounds, waterfall insets and CutoutPathParserInfo.
      *
      * @hide
      */
-    public static DisplayCutout fromBoundsAndWaterfall(Rect[] bounds, Insets waterfallInsets) {
-        return new DisplayCutout(ZERO_RECT, waterfallInsets, bounds, false /* copyArguments */);
+    public static DisplayCutout constructDisplayCutout(Rect[] bounds, Insets waterfallInsets,
+            CutoutPathParserInfo info) {
+        return new DisplayCutout(ZERO_RECT, waterfallInsets, bounds, info,
+                false /* copyArguments */);
     }
 
     /**
@@ -676,7 +868,8 @@
      * @hide
      */
     public static DisplayCutout fromBounds(Rect[] bounds) {
-        return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, false /* copyArguments */);
+        return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, null /* cutoutPathParserInfo */,
+                false /* copyArguments */);
     }
 
     /**
@@ -686,10 +879,12 @@
      *
      * @hide
      */
-    public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth, int displayHeight) {
-        return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation),
+    public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth,
+            int displayHeight) {
+        return pathAndDisplayCutoutFromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout),
+                res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation),
                 displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
-                loadWaterfallInset(res));
+                loadWaterfallInset(res)).second;
     }
 
     /**
@@ -699,7 +894,7 @@
      */
     public static Path pathFromResources(Resources res, int displayWidth, int displayHeight) {
         return pathAndDisplayCutoutFromSpec(
-                res.getString(R.string.config_mainBuiltInDisplayCutout),
+                res.getString(R.string.config_mainBuiltInDisplayCutout), null,
                 displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
                 loadWaterfallInset(res)).first;
     }
@@ -710,14 +905,30 @@
      * @hide
      */
     @VisibleForTesting(visibility = PRIVATE)
-    public static DisplayCutout fromSpec(String spec, int displayWidth, int displayHeight,
-            float density, Insets waterfallInsets) {
+    public static DisplayCutout fromSpec(String pathSpec, int displayWidth,
+            int displayHeight, float density, Insets waterfallInsets) {
         return pathAndDisplayCutoutFromSpec(
-                spec, displayWidth, displayHeight, density, waterfallInsets).second;
+                pathSpec, null, displayWidth, displayHeight, density, waterfallInsets)
+                .second;
     }
 
-    private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(String spec,
-            int displayWidth, int displayHeight, float density, Insets waterfallInsets) {
+    /**
+     * Gets the cutout path and the corresponding DisplayCutout instance from the spec string.
+     *
+     * @param pathSpec the spec string read from config_mainBuiltInDisplayCutout.
+     * @param rectSpec the spec string read from config_mainBuiltInDisplayCutoutRectApproximation.
+     * @param displayWidth the display width.
+     * @param displayHeight the display height.
+     * @param density the display density.
+     * @param waterfallInsets the waterfall insets of the display.
+     * @return a Pair contains the cutout path and the corresponding DisplayCutout instance.
+     */
+    private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(
+            String pathSpec, String rectSpec, int displayWidth, int displayHeight, float density,
+            Insets waterfallInsets) {
+        // Always use the rect approximation spec to create the cutout if it's not null because
+        // transforming and sending a Region constructed from a path is very costly.
+        String spec = rectSpec != null ? rectSpec : pathSpec;
         if (TextUtils.isEmpty(spec) && waterfallInsets.equals(Insets.NONE)) {
             return NULL_PAIR;
         }
@@ -750,9 +961,12 @@
                     Math.max(waterfallInsets.bottom, safeInset.bottom));
         }
 
+        final CutoutPathParserInfo cutoutPathParserInfo = new CutoutPathParserInfo(displayWidth,
+                displayHeight, density, pathSpec.trim(), ROTATION_0, 1f /* scale */);
+
         final DisplayCutout cutout = new DisplayCutout(
-                safeInset, waterfallInsets, boundLeft, boundTop,
-                boundRight, boundBottom, false /* copyArguments */);
+                safeInset, waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
+                cutoutPathParserInfo , false /* copyArguments */);
         final Pair<Path, DisplayCutout> result = new Pair<>(cutoutSpec.getPath(), cutout);
         synchronized (CACHE_LOCK) {
             sCachedSpec = spec;
@@ -817,6 +1031,12 @@
                 out.writeTypedObject(cutout.mSafeInsets, flags);
                 out.writeTypedArray(cutout.mBounds.getRects(), flags);
                 out.writeTypedObject(cutout.mWaterfallInsets, flags);
+                out.writeInt(cutout.mCutoutPathParserInfo.getDisplayWidth());
+                out.writeInt(cutout.mCutoutPathParserInfo.getDisplayHeight());
+                out.writeFloat(cutout.mCutoutPathParserInfo.getDensity());
+                out.writeString(cutout.mCutoutPathParserInfo.getCutoutSpec());
+                out.writeInt(cutout.mCutoutPathParserInfo.getRotation());
+                out.writeFloat(cutout.mCutoutPathParserInfo.getScale());
             }
         }
 
@@ -860,9 +1080,17 @@
             Rect[] bounds = new Rect[BOUNDS_POSITION_LENGTH];
             in.readTypedArray(bounds, Rect.CREATOR);
             Insets waterfallInsets = in.readTypedObject(Insets.CREATOR);
+            int displayWidth = in.readInt();
+            int displayHeight = in.readInt();
+            float density = in.readFloat();
+            String cutoutSpec = in.readString();
+            int rotation = in.readInt();
+            float scale = in.readFloat();
+            final CutoutPathParserInfo info = new CutoutPathParserInfo(
+                    displayWidth, displayHeight, density, cutoutSpec, rotation, scale);
 
             return new DisplayCutout(
-                    safeInsets, waterfallInsets, bounds, false /* copyArguments */);
+                    safeInsets, waterfallInsets, bounds, info, false /* copyArguments */);
         }
 
         public DisplayCutout get() {
@@ -884,7 +1112,15 @@
             bounds.scale(scale);
             final Rect waterfallInsets = mInner.mWaterfallInsets.toRect();
             waterfallInsets.scale(scale);
-            mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds);
+            final CutoutPathParserInfo info = new CutoutPathParserInfo(
+                    mInner.mCutoutPathParserInfo.getDisplayWidth(),
+                    mInner.mCutoutPathParserInfo.getDisplayHeight(),
+                    mInner.mCutoutPathParserInfo.getDensity(),
+                    mInner.mCutoutPathParserInfo.getCutoutSpec(),
+                    mInner.mCutoutPathParserInfo.getRotation(),
+                    scale);
+
+            mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, info);
         }
 
         @Override
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 106e392..0a1a231 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -414,6 +414,15 @@
      */
     public static final int SECURE = 0x00000080;
 
+
+    /**
+     * Queue up BufferStateLayer buffers instead of dropping the oldest buffer when this flag is
+     * set. This blocks the client until all the buffers have been presented. If the buffers
+     * have presentation timestamps, then we may drop buffers.
+     * @hide
+     */
+    public static final int ENABLE_BACKPRESSURE = 0x00000100;
+
     /**
      * Surface creation flag: Creates a surface where color components are interpreted
      * as "non pre-multiplied" by their alpha channel. Of course this flag is
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 0e878fc..4ef63ae 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -262,7 +262,7 @@
         }
     }
 
-    binder::Status onScreenCaptureComplete(
+    binder::Status onScreenCaptureCompleted(
             const gui::ScreenCaptureResults& captureResults) override {
         JNIEnv* env = getenv();
         if (captureResults.result != NO_ERROR || captureResults.buffer == nullptr) {
@@ -270,6 +270,7 @@
                                 gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr);
             return binder::Status::ok();
         }
+        captureResults.fence->waitForever("");
         jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
                 env, captureResults.buffer->toAHardwareBuffer());
         const jint namedColorSpace =
diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
index 24f45a5..bffd1e4 100644
--- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
+++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
@@ -28,6 +28,7 @@
 import static org.junit.Assert.fail;
 
 import android.content.pm.PackageParser.SigningDetails;
+import android.util.ArraySet;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
@@ -35,6 +36,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Set;
+
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class SigningDetailsTest {
@@ -208,8 +211,8 @@
         SigningDetails result1 = noLineageDetails.mergeLineageWith(lineageDetails);
         SigningDetails result2 = lineageDetails.mergeLineageWith(noLineageDetails);
 
-        assertTrue(result1 == lineageDetails);
-        assertTrue(result2 == lineageDetails);
+        assertSigningDetailsContainsLineage(result1, FIRST_SIGNATURE, SECOND_SIGNATURE);
+        assertSigningDetailsContainsLineage(result2, FIRST_SIGNATURE, SECOND_SIGNATURE);
     }
 
     @Test
@@ -271,8 +274,10 @@
         SigningDetails result1 = singleSignerDetails.mergeLineageWith(fullLineageDetails);
         SigningDetails result2 = fullLineageDetails.mergeLineageWith(singleSignerDetails);
 
-        assertTrue(result1 == fullLineageDetails);
-        assertTrue(result2 == fullLineageDetails);
+        assertSigningDetailsContainsLineage(result1, FIRST_SIGNATURE, SECOND_SIGNATURE,
+                THIRD_SIGNATURE);
+        assertSigningDetailsContainsLineage(result2, FIRST_SIGNATURE, SECOND_SIGNATURE,
+                THIRD_SIGNATURE);
     }
 
     @Test
@@ -605,6 +610,213 @@
         assertTrue(secondLineageDetails.hasCommonAncestor(firstLineageDetails));
     }
 
+    @Test
+    public void hasCommonSignerWithCapabilities_singleMatchingSigner_returnsTrue()
+            throws Exception {
+        // The hasCommonSignerWithCapabilities method is intended to grant the specified
+        // capabilities to a requesting package that has a common signer in the lineage (or as the
+        // current signer) even if their signing identities have diverged. This test verifies if the
+        // two SigningDetails have the same single signer then the requested capability can be
+        // granted since the current signer always has all capabilities granted.
+        SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE);
+        SigningDetails secondSignerDetails = createSigningDetails(FIRST_SIGNATURE);
+
+        assertTrue(firstDetails.hasCommonSignerWithCapability(secondSignerDetails, PERMISSION));
+    }
+
+    @Test
+    public void hasCommonSignerWithCapabilities_singleDifferentSigners_returnsFalse()
+            throws Exception {
+        // If each package is signed by a single different signer then the method should return
+        // false since there is no shared signer.
+        SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE);
+        SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE);
+
+        assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION));
+        assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION));
+    }
+
+    @Test
+    public void hasCommonSignerWithCapabilities_oneWithMultipleSigners_returnsFalse()
+            throws Exception {
+        // If one of the packages is signed with multiple signers and the other only a single signer
+        // this method should return false since all signers must match exactly for multiple signer
+        // cases.
+        SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE);
+        SigningDetails secondDetails = createSigningDetails(FIRST_SIGNATURE);
+
+        assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION));
+        assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION));
+    }
+
+    @Test
+    public void hasCommonSignerWithCapabilities_multipleMatchingSigners_returnsTrue()
+            throws Exception {
+        // if both packages are signed by the same multiple signers then this method should return
+        // true since the current signer is granted all capabilities.
+        SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE);
+        SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE, FIRST_SIGNATURE);
+
+        assertTrue(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION));
+        assertTrue(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION));
+    }
+
+    @Test
+    public void hasCommonSignerWithCapabilities_singleSignerInLineage_returnsTrue()
+            throws Exception {
+        // if a single signer is in the lineage and that previous signer has the requested
+        // capability then this method should return true.
+        SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE},
+                new int[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES});
+        SigningDetails singleSignerDetails = createSigningDetails(FIRST_SIGNATURE);
+
+        assertTrue(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION));
+    }
+
+    @Test
+    public void hasCommonSignerWithCapabilities_singleSignerInLineageWOCapability_returnsFalse()
+            throws Exception {
+        // If a single signer is in the lineage and that previous signer does not have the requested
+        // capability then this method should return false.
+        SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE},
+                new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES});
+        SigningDetails singleSignerDetails = createSigningDetails(FIRST_SIGNATURE);
+
+        assertFalse(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION));
+    }
+
+    @Test
+    public void hasCommonSignerWithCapabilities_singleSignerMatchesCurrentSigner_returnsTrue()
+            throws Exception {
+        // If a requesting app is signed by the same current signer as an app with a lineage the
+        // method should return true since the current signer is granted all capabilities.
+        SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE},
+                new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES});
+        SigningDetails singleSignerDetails = createSigningDetails(SECOND_SIGNATURE);
+
+        assertTrue(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION));
+    }
+
+    @Test
+    public void hasCommonSignerWithCapabilities_divergingSignersWithCommonSigner_returnsTrue()
+            throws Exception {
+        // This method is intended to allow granting a capability to another app that has a common
+        // signer in the lineage with the capability still granted; this test verifies when the
+        // current signers diverge but a common ancestor has the requested capability this method
+        // returns true.
+        SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+                new int[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES});
+        SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE, FOURTH_SIGNATURE);
+
+        assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails,
+                PERMISSION));
+    }
+
+    @Test
+    public void hasCommonSignerWithCapabilities_divergingSignersOneGrantsCapability_returnsTrue()
+            throws Exception {
+        // If apps have multiple common signers in the lineage with one denying the requested
+        // capability but the other granting it this method should return true.
+        SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+                new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES});
+        SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE, FOURTH_SIGNATURE);
+
+        assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails,
+                PERMISSION));
+    }
+
+    @Test
+    public void hasCommonSignerWithCapabilities_divergingSignersNoneGrantCapability_returnsFalse()
+            throws Exception {
+        // If apps have multiple common signers in the lineage with all denying the requested
+        // capability this method should return false.
+        SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+                new int[]{SHARED_USER_ID, AUTH, DEFAULT_CAPABILITIES});
+        SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE, FOURTH_SIGNATURE);
+
+        assertFalse(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails,
+                PERMISSION));
+    }
+
+    @Test
+    public void
+            hasCommonSignerWithCapabilities_divergingSignersNoneGrantsAllCapabilities_returnsTrue()
+            throws Exception {
+        // If an app has multiple common signers in the lineage, each granting one of the requested
+        // capabilities but neither granting all this method should return false since a single
+        // common ancestor must grant all requested capabilities.
+        SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+                new int[]{SHARED_USER_ID, PERMISSION, DEFAULT_CAPABILITIES});
+        SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE, FOURTH_SIGNATURE);
+
+        assertFalse(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails,
+                PERMISSION | SHARED_USER_ID));
+    }
+
+    @Test
+    public void hasCommonSignerWithCapabilities_currentSignerInLineageOfRequestingApp_returnsTrue()
+            throws Exception {
+        // If the current signer of an app is in the lineage of the requesting app then this method
+        // should return true since the current signer is granted all capabilities.
+        SigningDetails firstLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+        SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+        assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails,
+                PERMISSION));
+    }
+
+    @Test
+    public void hasCommonSignerWithCapabilities_currentSignerInLineageOfDeclaringApp_returnsTrue()
+            throws Exception {
+        // If the current signer of a requesting app with a lineage is in the lineage of the
+        // declaring app and that previous signature is granted the requested capability the method
+        // should return true.
+        SigningDetails declaringDetails = createSigningDetailsWithLineageAndCapabilities(
+                new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+                new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES});
+        SigningDetails requestingDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+                SECOND_SIGNATURE);
+
+        assertTrue(declaringDetails.hasCommonSignerWithCapability(requestingDetails, PERMISSION));
+    }
+
+    @Test
+    public void hasCommonSignerWithCapabilities_oneSignerNullLineage_returns() throws Exception {
+        // While the pastSigningCertificates should only be null in the case of multiple current
+        // signers there are instances where this can be null with a single signer; verify that a
+        // null pastSigningCertificates array in either SigningDetails does not result in a
+        // NullPointerException.
+        SigningDetails firstDetails = createSigningDetails(true, FIRST_SIGNATURE);
+        SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE);
+
+        assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION));
+        assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION));
+    }
+
+    @Test
+    public void hasCommonSignerWithCapabilities_unknownSigner_returnsFalse() throws Exception {
+        // An unknown SigningDetails for either instance should immediately result in false being
+        // returned.
+        SigningDetails firstDetails = SigningDetails.UNKNOWN;
+        SigningDetails secondDetails = createSigningDetails(FIRST_SIGNATURE);
+
+        assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION));
+        assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION));
+    }
+
     private SigningDetails createSigningDetailsWithLineage(String... signers) throws Exception {
         int[] capabilities = new int[signers.length];
         for (int i = 0; i < capabilities.length; i++) {
@@ -629,10 +841,34 @@
     }
 
     private SigningDetails createSigningDetails(String... signers) throws Exception {
+        return createSigningDetails(false, signers);
+    }
+
+    private SigningDetails createSigningDetails(boolean useNullPastSigners, String... signers)
+            throws Exception {
         Signature[] currentSignatures = new Signature[signers.length];
         for (int i = 0; i < signers.length; i++) {
             currentSignatures[i] = new Signature(signers[i]);
         }
-        return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, null);
+        // If there are multiple signers then the pastSigningCertificates should be set to null, but
+        // if there is only a single signer both the current signer and the past signers should be
+        // set to that one signer.
+        if (signers.length > 1) {
+            return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, null);
+        }
+        return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, currentSignatures);
+    }
+
+    private void assertSigningDetailsContainsLineage(SigningDetails details,
+            String... pastSigners) {
+        // This method should only be invoked for results that contain a single signer.
+        assertEquals(1, details.signatures.length);
+        assertTrue(details.signatures[0].toCharsString().equalsIgnoreCase(
+                pastSigners[pastSigners.length - 1]));
+        Set<String> signatures = new ArraySet<>(pastSigners);
+        for (Signature pastSignature : details.pastSigningCertificates) {
+            assertTrue(signatures.remove(pastSignature.toCharsString()));
+        }
+        assertEquals(0, signatures.size());
     }
 }
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index d02c6d5..a5261ae 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -22,6 +22,8 @@
 
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
 import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -30,6 +32,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.graphics.Insets;
+import android.graphics.Path;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
@@ -130,6 +133,8 @@
     @Test
     public void testHasCutout_noCutout() throws Exception {
         assertTrue(NO_CUTOUT.isBoundsEmpty());
+        assertThat(NO_CUTOUT.getWaterfallInsets(), equalTo(Insets.NONE));
+        assertThat(NO_CUTOUT.getCutoutPath(), nullValue());
     }
 
     @Test
@@ -165,6 +170,59 @@
     }
 
     @Test
+    public void testGetCutoutPath() throws Exception {
+        final String cutoutSpecString = "L1,0 L1,1 L0,1 z";
+        final int displayWidth = 200;
+        final int displayHeight = 400;
+        final float density = 1f;
+        final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight,
+                density, Insets.NONE);
+        assertThat(cutout.getCutoutPath(), notNullValue());
+    }
+
+    @Test
+    public void testGetCutoutPath_caches() throws Exception {
+        final String cutoutSpecString = "L1,0 L1,1 L0,1 z";
+        final int displayWidth = 200;
+        final int displayHeight = 400;
+        final float density = 1f;
+        final Path first = fromSpec(cutoutSpecString, displayWidth, displayHeight,
+                density, Insets.NONE).getCutoutPath();
+        final Path second = fromSpec(cutoutSpecString, displayWidth, displayHeight,
+                density, Insets.NONE).getCutoutPath();
+        assertThat(first, equalTo(second));
+    }
+
+    @Test
+    public void testGetCutoutPath_wontCacheIfCutoutPathParerInfoChanged() throws Exception {
+        final int displayWidth = 200;
+        final int displayHeight = 400;
+        final float density = 1f;
+        final Path first = fromSpec("L1,0 L1,1 L0,1 z", displayWidth, displayHeight,
+                density, Insets.NONE).getCutoutPath();
+        final Path second = fromSpec("L2,0 L2,2 L0,2 z", displayWidth, displayHeight,
+                density, Insets.NONE).getCutoutPath();
+        assertThat(first, not(equalTo(second)));
+    }
+
+    @Test
+    public void testGetCutoutPathParserInfo() throws Exception {
+        final String cutoutSpecString = "L1,0 L1,1 L0,1 z";
+        final int displayWidth = 200;
+        final int displayHeight = 400;
+        final float density = 1f;
+        final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight,
+                density, Insets.NONE);
+        assertThat(displayWidth, equalTo(cutout.getCutoutPathParserInfo().getDisplayWidth()));
+        assertThat(displayHeight, equalTo(cutout.getCutoutPathParserInfo().getDisplayHeight()));
+        assertThat(density, equalTo(cutout.getCutoutPathParserInfo().getDensity()));
+        assertThat(cutoutSpecString.trim(),
+                equalTo(cutout.getCutoutPathParserInfo().getCutoutSpec()));
+        assertThat(0, equalTo(cutout.getCutoutPathParserInfo().getRotation()));
+        assertThat(1f, equalTo(cutout.getCutoutPathParserInfo().getScale()));
+    }
+
+    @Test
     public void testHashCode() throws Exception {
         assertEquals(mCutoutWithWaterfall.hashCode(), createCutoutWithWaterfall().hashCode());
         assertNotEquals(mCutoutWithWaterfall.hashCode(), mCutoutNumbers.hashCode());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 1df2a4a..bb8a973 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -225,8 +225,9 @@
         mTaskOrganizer.applyTransaction(wct);
         // TODO(b/151449487): Only call callback once we enable synchronization
         if (mListener != null) {
+            final int taskId = mTaskInfo.taskId;
             mListenerExecutor.execute(() -> {
-                mListener.onTaskVisibilityChanged(mTaskInfo.taskId, mSurfaceCreated);
+                mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated);
             });
         }
     }
@@ -256,8 +257,10 @@
         }
 
         if (mListener != null) {
+            final int taskId = taskInfo.taskId;
+            final ComponentName baseActivity = taskInfo.baseActivity;
             mListenerExecutor.execute(() -> {
-                mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity);
+                mListener.onTaskCreated(taskId, baseActivity);
             });
         }
     }
@@ -267,8 +270,9 @@
         if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
 
         if (mListener != null) {
+            final int taskId = taskInfo.taskId;
             mListenerExecutor.execute(() -> {
-                mListener.onTaskRemovalStarted(taskInfo.taskId);
+                mListener.onTaskRemovalStarted(taskId);
             });
         }
         mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
@@ -289,8 +293,9 @@
     public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
         if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
         if (mListener != null) {
+            final int taskId = taskInfo.taskId;
             mListenerExecutor.execute(() -> {
-                mListener.onBackPressedOnTaskRoot(taskInfo.taskId);
+                mListener.onBackPressedOnTaskRoot(taskId);
             });
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 3181dbf..58a4baf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -356,11 +356,11 @@
         if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
             return null;
         }
-        final Insets waterfallInsets =
-                RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
         if (rotation == ROTATION_0) {
             return computeSafeInsets(cutout, displayWidth, displayHeight);
         }
+        final Insets waterfallInsets =
+                RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
         Rect[] cutoutRects = cutout.getBoundingRectsAll();
         final Rect[] newBounds = new Rect[cutoutRects.length];
@@ -372,8 +372,12 @@
             }
             newBounds[getBoundIndexFromRotation(i, rotation)] = rect;
         }
+        final DisplayCutout.CutoutPathParserInfo info = cutout.getCutoutPathParserInfo();
+        final DisplayCutout.CutoutPathParserInfo newInfo = new DisplayCutout.CutoutPathParserInfo(
+                info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
+                info.getCutoutSpec(), rotation, info.getScale());
         return computeSafeInsets(
-                DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets),
+                DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo),
                 rotated ? displayHeight : displayWidth,
                 rotated ? displayWidth : displayHeight);
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
index 4874d3c..a4cd3c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
@@ -47,6 +47,11 @@
     }
 
     @Override
+    public void removeAllCallbacks() {
+        mHandler.removeCallbacksAndMessages(null);
+    }
+
+    @Override
     public void removeCallbacks(@NonNull Runnable r) {
         mHandler.removeCallbacks(r);
     }
@@ -55,9 +60,4 @@
     public boolean hasCallback(Runnable r) {
         return mHandler.hasCallbacks(r);
     }
-
-    @Override
-    public Looper getLooper() {
-        return mHandler.getLooper();
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
index 1149cce..b736fb0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
@@ -73,6 +73,11 @@
     void executeDelayed(Runnable runnable, long delayMillis);
 
     /**
+     * Removes all pending callbacks.
+     */
+    void removeAllCallbacks();
+
+    /**
      * See {@link android.os.Handler#removeCallbacks}.
      */
     void removeCallbacks(Runnable runnable);
@@ -81,9 +86,4 @@
      * See {@link android.os.Handler#hasCallbacks(Runnable)}.
      */
     boolean hasCallback(Runnable runnable);
-
-    /**
-     * Returns the looper that this executor is running on.
-     */
-    Looper getLooper();
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
index a74f476..37a91d0c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
@@ -56,7 +56,7 @@
     private final float[] mColor;
     private final float mAlpha;
     private final Rect mRect;
-    private final Handler mHandler;
+    private final Executor mMainExecutor;
     private final Point mDisplaySize = new Point();
     private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
             mSurfaceControlTransactionFactory;
@@ -76,13 +76,13 @@
                 @Override
                 public void onOneHandedAnimationStart(
                         OneHandedAnimationController.OneHandedTransitionAnimator animator) {
-                    mHandler.post(() -> showBackgroundPanelLayer());
+                    mMainExecutor.execute(() -> showBackgroundPanelLayer());
                 }
             };
 
     @Override
     public void onStopFinished(Rect bounds) {
-        mHandler.post(() -> removeBackgroundPanelLayer());
+        mMainExecutor.execute(() -> removeBackgroundPanelLayer());
     }
 
     public OneHandedBackgroundPanelOrganizer(Context context, DisplayController displayController,
@@ -94,7 +94,7 @@
         mColor = new float[]{defaultRGB, defaultRGB, defaultRGB};
         mAlpha = res.getFloat(R.dimen.config_one_handed_background_alpha);
         mRect = new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
-        mHandler = new Handler();
+        mMainExecutor = executor;
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
index 1ed121f..49b7e05 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
@@ -221,8 +221,14 @@
                     displaySize.y);
             mInputMonitor = InputManager.getInstance().monitorGestureInput(
                     "onehanded-gesture-offset", DEFAULT_DISPLAY);
-            mInputEventReceiver = new EventReceiver(
-                    mInputMonitor.getInputChannel(), mMainExecutor.getLooper());
+            try {
+                mMainExecutor.executeBlocking(() -> {
+                    mInputEventReceiver = new EventReceiver(
+                            mInputMonitor.getInputChannel(), Looper.myLooper());
+                });
+            } catch (InterruptedException e) {
+                throw new RuntimeException("Failed to create input event receiver", e);
+            }
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
index 60709be..c7a49ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
@@ -132,8 +132,14 @@
         if (mIsEnabled) {
             mInputMonitor = InputManager.getInstance().monitorGestureInput(
                     "onehanded-touch", DEFAULT_DISPLAY);
-            mInputEventReceiver = new EventReceiver(
-                    mInputMonitor.getInputChannel(), mMainExecutor.getLooper());
+            try {
+                mMainExecutor.executeBlocking(() -> {
+                    mInputEventReceiver = new EventReceiver(
+                            mInputMonitor.getInputChannel(), Looper.myLooper());
+                });
+            } catch (InterruptedException e) {
+                throw new RuntimeException("Failed to create input event receiver", e);
+            }
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
index 7a634c3..6e3a20d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
@@ -147,7 +147,7 @@
             // Choreographer.getSfInstance() must be called on the thread that the input event
             // receiver should be receiving events
             mInputEventReceiver = new InputEventReceiver(inputChannel,
-                mMainExecutor.getLooper(), Choreographer.getSfInstance());
+                Looper.myLooper(), Choreographer.getSfInstance());
             if (mRegistrationListener != null) {
                 mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 41cc59d..8fb358a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -212,8 +212,14 @@
             // Register input event receiver
             mInputMonitor = InputManager.getInstance().monitorGestureInput(
                     "pip-resize", mDisplayId);
-            mInputEventReceiver = new PipResizeInputEventReceiver(
-                    mInputMonitor.getInputChannel(), mMainExecutor.getLooper());
+            try {
+                mMainExecutor.executeBlocking(() -> {
+                    mInputEventReceiver = new PipResizeInputEventReceiver(
+                            mInputMonitor.getInputChannel(), Looper.myLooper());
+                });
+            } catch (InterruptedException e) {
+                throw new RuntimeException("Failed to create input event receiver", e);
+            }
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index c9f5ae2..2b8b53c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -95,7 +95,6 @@
     private int mDeferResizeToNormalBoundsUntilRotation = -1;
     private int mDisplayRotation;
 
-    private final Handler mHandler = new Handler();
     private final PipAccessibilityInteractionConnection mConnection;
 
     // Behaviour states
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
index 5f5c30b..bf84a6e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
@@ -40,6 +40,11 @@
     }
 
     @Override
+    public void removeAllCallbacks() {
+        mRunnables.clear();
+    }
+
+    @Override
     public void removeCallbacks(Runnable r) {
         mRunnables.remove(r);
     }
@@ -49,11 +54,6 @@
         return mRunnables.contains(r);
     }
 
-    @Override
-    public Looper getLooper() {
-        return null;
-    }
-
     public void flushAll() {
         for (Runnable r : mRunnables) {
             r.run();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
index 9219f15..bbe8891 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
@@ -33,6 +33,7 @@
 
 import androidx.test.filters.SmallTest;
 
+import com.android.wm.shell.TestShellExecutor;
 import com.android.wm.shell.common.ShellExecutor;
 
 import org.junit.Before;
@@ -48,7 +49,7 @@
 @RunWith(AndroidTestingRunner.class)
 public class OneHandedTimeoutHandlerTest extends OneHandedTestCase {
     private OneHandedTimeoutHandler mTimeoutHandler;
-    private ShellExecutor mMainExecutor;
+    private TestShellExecutor mMainExecutor;
 
     @Before
     public void setUp() throws Exception {
@@ -104,34 +105,4 @@
         mTimeoutHandler.resetTimer();
         assertTrue(mTimeoutHandler.hasScheduledTimeout());
     }
-
-    private class TestShellExecutor implements ShellExecutor {
-        private ArrayList<Runnable> mExecuted = new ArrayList<>();
-        private ArrayList<Runnable> mDelayed = new ArrayList<>();
-
-        @Override
-        public void execute(Runnable runnable) {
-            mExecuted.add(runnable);
-        }
-
-        @Override
-        public void executeDelayed(Runnable r, long delayMillis) {
-            mDelayed.add(r);
-        }
-
-        @Override
-        public void removeCallbacks(Runnable r) {
-            mDelayed.remove(r);
-        }
-
-        @Override
-        public boolean hasCallback(Runnable r) {
-            return mDelayed.contains(r);
-        }
-
-        @Override
-        public Looper getLooper() {
-            return Looper.myLooper();
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
index 93a8df4..cd3d6a8 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
@@ -26,8 +26,7 @@
     private String mPackageName;
     private long mTimeStarted;
     private StringBuilder mState;
-    // This is only used for items with mCode == AppOpsManager.OP_RECORD_AUDIO
-    private boolean mSilenced;
+    private boolean mIsDisabled;
 
     public AppOpItem(int code, int uid, String packageName, long timeStarted) {
         this.mCode = code;
@@ -58,16 +57,16 @@
         return mTimeStarted;
     }
 
-    public void setSilenced(boolean silenced) {
-        mSilenced = silenced;
+    public void setDisabled(boolean misDisabled) {
+        this.mIsDisabled = misDisabled;
     }
 
-    public boolean isSilenced() {
-        return mSilenced;
+    public boolean isDisabled() {
+        return mIsDisabled;
     }
 
     @Override
     public String toString() {
-        return mState.append(mSilenced).append(")").toString();
+        return mState.append(mIsDisabled).append(")").toString();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 1036c99..d8ca639 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.appops;
 
+import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
+import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
 import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED;
 
 import android.Manifest;
@@ -45,6 +47,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
 import com.android.systemui.util.Assert;
 
 import java.io.FileDescriptor;
@@ -64,7 +67,8 @@
 @SysUISingleton
 public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsController,
         AppOpsManager.OnOpActiveChangedInternalListener,
-        AppOpsManager.OnOpNotedListener, Dumpable {
+        AppOpsManager.OnOpNotedListener, IndividualSensorPrivacyController.Callback,
+        Dumpable {
 
     // This is the minimum time that we will keep AppOps that are noted on record. If multiple
     // occurrences of the same (op, package, uid) happen in a shorter interval, they will not be
@@ -77,8 +81,8 @@
     private final AppOpsManager mAppOps;
     private final AudioManager mAudioManager;
     private final LocationManager mLocationManager;
-    // TODO ntmyren: remove t
     private final PackageManager mPackageManager;
+    private final IndividualSensorPrivacyController mSensorPrivacyController;
 
     // mLocationProviderPackages are cached and updated only occasionally
     private static final long LOCATION_PROVIDER_UPDATE_FREQUENCY_MS = 30000;
@@ -91,6 +95,7 @@
     private final PermissionFlagsCache mFlagsCache;
     private boolean mListening;
     private boolean mMicMuted;
+    private boolean mCameraDisabled;
 
     @GuardedBy("mActiveItems")
     private final List<AppOpItem> mActiveItems = new ArrayList<>();
@@ -118,6 +123,7 @@
             DumpManager dumpManager,
             PermissionFlagsCache cache,
             AudioManager audioManager,
+            IndividualSensorPrivacyController sensorPrivacyController,
             BroadcastDispatcher dispatcher
     ) {
         mDispatcher = dispatcher;
@@ -129,7 +135,10 @@
             mCallbacksByCode.put(OPS[i], new ArraySet<>());
         }
         mAudioManager = audioManager;
-        mMicMuted = audioManager.isMicrophoneMute();
+        mSensorPrivacyController = sensorPrivacyController;
+        mMicMuted = audioManager.isMicrophoneMute()
+                || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
+        mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA);
         mLocationManager = context.getSystemService(LocationManager.class);
         mPackageManager = context.getPackageManager();
         dumpManager.registerDumpable(TAG, this);
@@ -147,6 +156,12 @@
             mAppOps.startWatchingActive(OPS, this);
             mAppOps.startWatchingNoted(OPS, this);
             mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
+            mSensorPrivacyController.addCallback(this);
+
+            mMicMuted = mAudioManager.isMicrophoneMute()
+                    || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
+            mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA);
+
             mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
                     mAudioManager.getActiveRecordingConfigurations()));
             mDispatcher.registerReceiverWithHandler(this,
@@ -156,6 +171,7 @@
             mAppOps.stopWatchingActive(this);
             mAppOps.stopWatchingNoted(this);
             mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
+            mSensorPrivacyController.removeCallback(this);
 
             mBGHandler.removeCallbacksAndMessages(null); // null removes all
             mDispatcher.unregisterReceiver(this);
@@ -235,11 +251,13 @@
             if (item == null && active) {
                 item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
                 if (code == AppOpsManager.OP_RECORD_AUDIO) {
-                    item.setSilenced(isAnyRecordingPausedLocked(uid));
+                    item.setDisabled(isAnyRecordingPausedLocked(uid));
+                } else if (code == AppOpsManager.OP_CAMERA) {
+                    item.setDisabled(mCameraDisabled);
                 }
                 mActiveItems.add(item);
                 if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
-                return !item.isSilenced();
+                return !item.isDisabled();
             } else if (item != null && !active) {
                 mActiveItems.remove(item);
                 if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
@@ -409,7 +427,7 @@
                 AppOpItem item = mActiveItems.get(i);
                 if ((userId == UserHandle.USER_ALL
                         || UserHandle.getUserId(item.getUid()) == userId)
-                        && isUserVisible(item) && !item.isSilenced()) {
+                        && isUserVisible(item) && !item.isDisabled()) {
                     list.add(item);
                 }
             }
@@ -512,22 +530,27 @@
         return false;
     }
 
-    private void updateRecordingPausedStatus() {
+    private void updateSensorDisabledStatus() {
         synchronized (mActiveItems) {
             int size = mActiveItems.size();
             for (int i = 0; i < size; i++) {
                 AppOpItem item = mActiveItems.get(i);
+
+                boolean paused = false;
                 if (item.getCode() == AppOpsManager.OP_RECORD_AUDIO) {
-                    boolean paused = isAnyRecordingPausedLocked(item.getUid());
-                    if (item.isSilenced() != paused) {
-                        item.setSilenced(paused);
-                        notifySuscribers(
-                                item.getCode(),
-                                item.getUid(),
-                                item.getPackageName(),
-                                !item.isSilenced()
-                        );
-                    }
+                    paused = isAnyRecordingPausedLocked(item.getUid());
+                } else if (item.getCode() == AppOpsManager.OP_CAMERA) {
+                    paused = mCameraDisabled;
+                }
+
+                if (item.isDisabled() != paused) {
+                    item.setDisabled(paused);
+                    notifySuscribers(
+                            item.getCode(),
+                            item.getUid(),
+                            item.getPackageName(),
+                            !item.isDisabled()
+                    );
                 }
             }
         }
@@ -552,14 +575,27 @@
                     recordings.add(recording);
                 }
             }
-            updateRecordingPausedStatus();
+            updateSensorDisabledStatus();
         }
     };
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        mMicMuted = mAudioManager.isMicrophoneMute();
-        updateRecordingPausedStatus();
+        mMicMuted = mAudioManager.isMicrophoneMute()
+                || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
+        updateSensorDisabledStatus();
+    }
+
+    @Override
+    public void onSensorBlockedChanged(int sensor, boolean blocked) {
+        mBGHandler.post(() -> {
+            if (sensor == INDIVIDUAL_SENSOR_CAMERA) {
+                mCameraDisabled = blocked;
+            } else if (sensor == INDIVIDUAL_SENSOR_MICROPHONE) {
+                mMicMuted = mAudioManager.isMicrophoneMute() || blocked;
+            }
+            updateSensorDisabledStatus();
+        });
     }
 
     protected class H extends Handler {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index c2c6790..9be3566 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -23,7 +23,6 @@
 import android.net.Uri;
 import android.os.UserHandle;
 import android.util.Log;
-import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewTreeObserver.InternalInsetsInfo;
 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
@@ -59,7 +58,6 @@
     private final Executor mBgExecutor;
     private final ImageExporter mImageExporter;
     private final ImageTileSet mImageTileSet;
-    private final LayoutInflater mLayoutInflater;
 
     private ZonedDateTime mCaptureTime;
     private UUID mRequestId;
@@ -81,7 +79,6 @@
         mBgExecutor = bgExecutor;
         mImageExporter = exporter;
         mImageTileSet = new ImageTileSet();
-        mLayoutInflater = mContext.getSystemService(LayoutInflater.class);
     }
 
     /**
@@ -114,7 +111,7 @@
         mEdit.setOnClickListener(this::onClicked);
         mShare.setOnClickListener(this::onClicked);
 
-        mPreview.setImageDrawable(mImageTileSet.getDrawable());
+        //mPreview.setImageDrawable(mImageTileSet.getDrawable());
         mConnection.start(this::startCapture);
     }
 
@@ -242,6 +239,7 @@
         if (mImageTileSet.isEmpty()) {
             session.end(mCallback::onFinish);
         } else {
+            mPreview.setImageDrawable(mImageTileSet.getDrawable());
             mExportFuture = mImageExporter.export(
                     mBgExecutor, mRequestId, mImageTileSet.toBitmap(), mCaptureTime);
             // The user chose an action already, link it to the result
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
index 231fe08..32d15ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.statusbar.policy;
 
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA;
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
+import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
+import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
 
 import android.hardware.SensorPrivacyManager;
 import android.hardware.SensorPrivacyManager.IndividualSensor;
@@ -30,7 +30,8 @@
 
 public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPrivacyController {
 
-    private static final int[] SENSORS = new int[] {CAMERA, MICROPHONE};
+    private static final int[] SENSORS = new int[] {INDIVIDUAL_SENSOR_CAMERA,
+            INDIVIDUAL_SENSOR_MICROPHONE};
 
     private final @NonNull SensorPrivacyManager mSensorPrivacyManager;
     private final SparseBooleanArray mState = new SparseBooleanArray();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 02143a7..bc322f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.appops;
 
+import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
+import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
+
 import static junit.framework.TestCase.assertFalse;
 
 import static org.junit.Assert.assertEquals;
@@ -49,6 +52,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -81,6 +85,8 @@
     private PermissionFlagsCache mFlagsCache;
     @Mock
     private PackageManager mPackageManager;
+    @Mock
+    private IndividualSensorPrivacyController mSensorPrivacyController;
     @Mock(stubOnly = true)
     private AudioManager mAudioManager;
     @Mock()
@@ -118,12 +124,18 @@
         when(mAudioManager.getActiveRecordingConfigurations())
                 .thenReturn(List.of(mPausedMockRecording));
 
+        when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA))
+                .thenReturn(false);
+        when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA))
+                .thenReturn(false);
+
         mController = new AppOpsControllerImpl(
                 mContext,
                 mTestableLooper.getLooper(),
                 mDumpManager,
                 mFlagsCache,
                 mAudioManager,
+                mSensorPrivacyController,
                 mDispatcher
         );
     }
@@ -133,6 +145,7 @@
         mController.setListening(true);
         verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController);
         verify(mDispatcher, times(1)).registerReceiverWithHandler(eq(mController), any(), any());
+        verify(mSensorPrivacyController, times(1)).addCallback(mController);
     }
 
     @Test
@@ -140,6 +153,7 @@
         mController.setListening(false);
         verify(mAppOpsManager, times(1)).stopWatchingActive(mController);
         verify(mDispatcher, times(1)).unregisterReceiver(mController);
+        verify(mSensorPrivacyController, times(1)).removeCallback(mController);
     }
 
     @Test
@@ -476,6 +490,71 @@
                 AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, false);
     }
 
+    @Test
+    public void testAudioFilteredWhenMicDisabled() {
+        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA},
+                mCallback);
+        mTestableLooper.processAllMessages();
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+        List<AppOpItem> list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+        assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode());
+        assertFalse(list.get(0).isDisabled());
+
+        // Add a camera op, and disable the microphone. The camera op should be the only op returned
+        mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, true);
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+        list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+        assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode());
+
+
+        // Re enable the microphone, and verify the op returns
+        mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, false);
+        mTestableLooper.processAllMessages();
+
+        list = mController.getActiveAppOps();
+        assertEquals(2, list.size());
+        int micIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 1 : 0;
+        assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(micIdx).getCode());
+    }
+
+    @Test
+    public void testCameraFilteredWhenCameraDisabled() {
+        mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA},
+                mCallback);
+        mTestableLooper.processAllMessages();
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+        List<AppOpItem> list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+        assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode());
+        assertFalse(list.get(0).isDisabled());
+
+        // Add an audio op, and disable the camera. The audio op should be the only op returned
+        mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, true);
+        mController.onOpActiveChanged(
+                AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+        mTestableLooper.processAllMessages();
+        list = mController.getActiveAppOps();
+        assertEquals(1, list.size());
+        assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode());
+
+        // Re enable the camera, and verify the op returns
+        mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, false);
+        mTestableLooper.processAllMessages();
+
+        list = mController.getActiveAppOps();
+        assertEquals(2, list.size());
+        int cameraIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 0 : 1;
+        assertEquals(AppOpsManager.OP_CAMERA, list.get(cameraIdx).getCode());
+    }
+
     private class TestHandler extends AppOpsControllerImpl.H {
         TestHandler(Looper looper) {
             mController.super(looper);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index d9ee9a3..13dc0b9 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -710,9 +710,9 @@
         }
     }
 
-    private void initialize() {
+    private void initialize(int displayState) {
         mPowerState = new DisplayPowerState(mBlanker,
-                mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId);
+                mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState);
 
         if (mColorFadeEnabled) {
             mColorFadeOnAnimator = ObjectAnimator.ofFloat(
@@ -812,11 +812,6 @@
             mustNotify = !mDisplayReadyLocked;
         }
 
-        // Initialize things the first time the power state is changed.
-        if (mustInitialize) {
-            initialize();
-        }
-
         // Compute the basic display state using the policy.
         // We might override this below based on other factors.
         // Initialise brightness as invalid.
@@ -850,6 +845,11 @@
         }
         assert(state != Display.STATE_UNKNOWN);
 
+        // Initialize things the first time the power state is changed.
+        if (mustInitialize) {
+            initialize(state);
+        }
+
         // Apply the proximity sensor.
         if (mProximitySensor != null) {
             if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 54f30a9..173adce 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -72,7 +72,8 @@
 
     private Runnable mCleanListener;
 
-    public DisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, int displayId) {
+    DisplayPowerState(
+            DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) {
         mHandler = new Handler(true /*async*/);
         mChoreographer = Choreographer.getInstance();
         mBlanker = blanker;
@@ -81,14 +82,14 @@
         mPhotonicModulator.start();
         mDisplayId = displayId;
 
-        // At boot time, we know that the screen is on and the electron beam
-        // animation is not playing.  We don't know the screen's brightness though,
+        // At boot time, we don't know the screen's brightness,
         // so prepare to set it to a known state when the state is next applied.
-        // Although we set the brightness to full on here, the display power controller
+        // Although we set the brightness here, the display power controller
         // will reset the brightness to a new level immediately before the changes
         // actually have a chance to be applied.
-        mScreenState = Display.STATE_ON;
-        mScreenBrightness = PowerManager.BRIGHTNESS_MAX;
+        mScreenState = displayState;
+        mScreenBrightness = (displayState != Display.STATE_OFF) ? PowerManager.BRIGHTNESS_MAX
+                : PowerManager.BRIGHTNESS_OFF_FLOAT;
         scheduleScreenUpdate();
 
         mColorFadePrepared = false;
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index b5b93d6..142f64f 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -74,7 +74,6 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.os.WorkSource.WorkChain;
@@ -1297,10 +1296,7 @@
                 return null;
             }
 
-            long currentNanos = SystemClock.elapsedRealtimeNanos();
-            long deltaMs = NANOSECONDS.toMillis(
-                    location.getElapsedRealtimeAgeNanos(currentNanos));
-            return new LocationTime(location.getTime() + deltaMs, currentNanos);
+            return new LocationTime(location.getTime(), location.getElapsedRealtimeNanos());
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c27e670..d2fc5b4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2153,6 +2153,10 @@
         void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
                 boolean requireFullPermission, boolean checkShell,
                 boolean requirePermissionWhenSameUser, String message);
+        SigningDetails getSigningDetails(@NonNull String packageName);
+        SigningDetails getSigningDetails(int uid);
+        boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId);
+        boolean filterAppAccess(String packageName, int callingUid, int userId);
     }
 
     /**
@@ -4578,6 +4582,40 @@
             throw new SecurityException(errorMessage);
         }
 
+        public SigningDetails getSigningDetails(@NonNull String packageName) {
+            AndroidPackage p = mPackages.get(packageName);
+            if (p == null) {
+                return null;
+            }
+            return p.getSigningDetails();
+        }
+
+        public SigningDetails getSigningDetails(int uid) {
+            final int appId = UserHandle.getAppId(uid);
+            final Object obj = mSettings.getSettingLPr(appId);
+            if (obj != null) {
+                if (obj instanceof SharedUserSetting) {
+                    return ((SharedUserSetting) obj).signatures.mSigningDetails;
+                } else if (obj instanceof PackageSetting) {
+                    final PackageSetting ps = (PackageSetting) obj;
+                    return ps.signatures.mSigningDetails;
+                }
+            }
+            return SigningDetails.UNKNOWN;
+        }
+
+        public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
+            PackageSetting ps = getPackageSetting(pkg.getPackageName());
+            return shouldFilterApplicationLocked(ps, callingUid,
+                    userId);
+        }
+
+        public boolean filterAppAccess(String packageName, int callingUid, int userId) {
+            PackageSetting ps = getPackageSetting(packageName);
+            return shouldFilterApplicationLocked(ps, callingUid,
+                    userId);
+        }
+
     }
 
     /**
@@ -4728,6 +4766,26 @@
                 return super.getPackageUidInternal(packageName, flags, userId, callingUid);
             }
         }
+        public SigningDetails getSigningDetails(@NonNull String packageName) {
+            synchronized (mLock) {
+                return super.getSigningDetails(packageName);
+            }
+        }
+        public SigningDetails getSigningDetails(int uid) {
+            synchronized (mLock) {
+                return super.getSigningDetails(uid);
+            }
+        }
+        public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
+            synchronized (mLock) {
+                return super.filterAppAccess(pkg, callingUid, userId);
+            }
+        }
+        public boolean filterAppAccess(String packageName, int callingUid, int userId) {
+            synchronized (mLock) {
+                return super.filterAppAccess(packageName, callingUid, userId);
+            }
+        }
     }
 
 
@@ -26560,6 +26618,22 @@
         return snapshotComputer().getPackage(uid);
     }
 
+    private SigningDetails getSigningDetails(@NonNull String packageName) {
+        return snapshotComputer().getSigningDetails(packageName);
+    }
+
+    private SigningDetails getSigningDetails(int uid) {
+        return snapshotComputer().getSigningDetails(uid);
+    }
+
+    private boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
+        return snapshotComputer().filterAppAccess(pkg, callingUid, userId);
+    }
+
+    private boolean filterAppAccess(String packageName, int callingUid, int userId) {
+        return snapshotComputer().filterAppAccess(packageName, callingUid, userId);
+    }
+
     private class PackageManagerInternalImpl extends PackageManagerInternal {
         @Override
         public List<ApplicationInfo> getInstalledApplications(int flags, int userId,
@@ -26615,29 +26689,11 @@
         }
 
         private SigningDetails getSigningDetails(@NonNull String packageName) {
-            synchronized (mLock) {
-                AndroidPackage p = mPackages.get(packageName);
-                if (p == null) {
-                    return null;
-                }
-                return p.getSigningDetails();
-            }
+            return PackageManagerService.this.getSigningDetails(packageName);
         }
 
         private SigningDetails getSigningDetails(int uid) {
-            synchronized (mLock) {
-                final int appId = UserHandle.getAppId(uid);
-                final Object obj = mSettings.getSettingLPr(appId);
-                if (obj != null) {
-                    if (obj instanceof SharedUserSetting) {
-                        return ((SharedUserSetting) obj).signatures.mSigningDetails;
-                    } else if (obj instanceof PackageSetting) {
-                        final PackageSetting ps = (PackageSetting) obj;
-                        return ps.signatures.mSigningDetails;
-                    }
-                }
-                return SigningDetails.UNKNOWN;
-            }
+            return PackageManagerService.this.getSigningDetails(uid);
         }
 
         @Override
@@ -26652,20 +26708,12 @@
 
         @Override
         public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
-            synchronized (mLock) {
-                PackageSetting ps = getPackageSetting(pkg.getPackageName());
-                return PackageManagerService.this.shouldFilterApplicationLocked(ps, callingUid,
-                        userId);
-            }
+            return PackageManagerService.this.filterAppAccess(pkg, callingUid, userId);
         }
 
         @Override
         public boolean filterAppAccess(String packageName, int callingUid, int userId) {
-            synchronized (mLock) {
-                PackageSetting ps = getPackageSetting(packageName);
-                return PackageManagerService.this.shouldFilterApplicationLocked(ps, callingUid,
-                        userId);
-            }
+            return PackageManagerService.this.filterAppAccess(packageName, callingUid, userId);
         }
 
         @Override
@@ -28304,6 +28352,13 @@
                     }
                     continue;
                 }
+                if (ps.appId < Process.FIRST_APPLICATION_UID) {
+                    if (DEBUG_PER_UID_READ_TIMEOUTS) {
+                        Slog.i(TAG, "PerUidReadTimeouts: package is system, appId=" + ps.appId);
+                    }
+                    continue;
+                }
+
                 final AndroidPackage pkg = ps.getPkg();
                 if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode
                         || pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 8c31d88..aff87111 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -3356,11 +3356,12 @@
         //     - or its signing certificate was rotated from the source package's certificate
         //     - or its signing certificate is a previous signing certificate of the defining
         //       package, and the defining package still trusts the old certificate for permissions
+        //     - or it shares a common signing certificate in its lineage with the defining package,
+        //       and the defining package still trusts the old certificate for permissions
         //     - or it shares the above relationships with the system package
         final PackageParser.SigningDetails sourceSigningDetails =
                 getSourcePackageSigningDetails(bp);
-        return pkg.getSigningDetails().hasAncestorOrSelf(sourceSigningDetails)
-                || sourceSigningDetails.checkCapability(
+        return sourceSigningDetails.hasCommonSignerWithCapability(
                         pkg.getSigningDetails(),
                         PackageParser.SigningDetails.CertCapabilities.PERMISSION)
                 || pkg.getSigningDetails().hasAncestorOrSelf(systemPackage.getSigningDetails())
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c073b43..89e7986 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3491,15 +3491,27 @@
     /** {@inheritDoc} */
     @Override
     public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
+        final int keyCode = event.getKeyCode();
+        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+        boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
+                || event.isWakeKey();
+
         if (!mSystemBooted) {
             // If we have not yet booted, don't let key events do anything.
+            // Exception: Wake and power key events are forwarded to PowerManager to allow it to
+            // wake from quiescent mode during boot.
+            if (down && (keyCode == KeyEvent.KEYCODE_POWER
+                    || keyCode == KeyEvent.KEYCODE_TV_POWER)) {
+                wakeUpFromPowerKey(event.getDownTime());
+            } else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP)
+                    && isWakeKeyWhenScreenOff(keyCode)) {
+                wakeUpFromWakeKey(event);
+            }
             return 0;
         }
 
         final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
-        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
         final boolean canceled = event.isCanceled();
-        final int keyCode = event.getKeyCode();
         final int displayId = event.getDisplayId();
         final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
 
@@ -3518,8 +3530,6 @@
 
         // Basic policy based on interactive state.
         int result;
-        boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
-                || event.isWakeKey();
         if (interactive || (isInjected && !isWakeKey)) {
             // When the device is interactive or the key is injected pass the
             // key to the application.
@@ -4740,7 +4750,7 @@
         }
         startedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
         finishedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
-        screenTurningOn(DEFAULT_DISPLAY, null);
+        screenTurningOn(DEFAULT_DISPLAY, mDefaultDisplayPolicy.getScreenOnListener());
         screenTurnedOn(DEFAULT_DISPLAY);
     }
 
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 54d0512..db4b6d0 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1036,12 +1036,12 @@
                 userActivityNoUpdateLocked(
                         now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
 
+                updatePowerStateLocked();
                 if (sQuiescent) {
                     goToSleepNoUpdateLocked(mClock.uptimeMillis(),
                             PowerManager.GO_TO_SLEEP_REASON_QUIESCENT,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
                 }
-                updatePowerStateLocked();
             }
         }
     }
@@ -1679,8 +1679,15 @@
             Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid);
         }
 
-        if (eventTime < mLastSleepTime || getWakefulnessLocked() == WAKEFULNESS_AWAKE
-                || mForceSuspendActive || !mSystemReady) {
+        if (eventTime < mLastSleepTime || mForceSuspendActive || !mSystemReady) {
+            return false;
+        }
+
+        if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) {
+            if (!mBootCompleted && sQuiescent) {
+                mDirty |= DIRTY_QUIESCENT;
+                return true;
+            }
             return false;
         }
 
@@ -2821,7 +2828,7 @@
      *
      * This function recalculates the display power state each time.
      *
-     * @return True if the display became ready.
+     * @return true if the display became ready.
      */
     private boolean updateDisplayPowerStateLocked(int dirty) {
         final boolean oldDisplayReady = mDisplayReady;
@@ -2830,7 +2837,11 @@
                 | DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED |
                 DIRTY_QUIESCENT)) != 0) {
             if ((dirty & DIRTY_QUIESCENT) != 0) {
-                sQuiescent = false;
+                if (mDisplayReady) {
+                    sQuiescent = false;
+                } else {
+                    mDirty |= DIRTY_QUIESCENT;
+                }
             }
 
             final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get(
@@ -5605,7 +5616,7 @@
      * ignore the proximity sensor.  We don't turn off the proximity sensor because
      * we still want it to be reenabled if it's state changes.
      *
-     * @return True if the proximity sensor was successfully ignored and we should
+     * @return true if the proximity sensor was successfully ignored and we should
      * consume the key event.
      */
     private boolean interceptPowerKeyDownInternal(KeyEvent event) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index f293fc3..0aaa1a1 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -190,6 +190,7 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 import android.view.DisplayCutout;
+import android.view.DisplayCutout.CutoutPathParserInfo;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IDisplayWindowInsetsController;
@@ -1934,18 +1935,22 @@
         if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
             return WmDisplayCutout.NO_CUTOUT;
         }
-        final Insets waterfallInsets =
-                RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
         if (rotation == ROTATION_0) {
             return WmDisplayCutout.computeSafeInsets(
                     cutout, mInitialDisplayWidth, mInitialDisplayHeight);
         }
+        final Insets waterfallInsets =
+                RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
         final Rect[] newBounds = mRotationUtil.getRotatedBounds(
                 cutout.getBoundingRectsAll(),
                 rotation, mInitialDisplayWidth, mInitialDisplayHeight);
+        final CutoutPathParserInfo info = cutout.getCutoutPathParserInfo();
+        final CutoutPathParserInfo newInfo = new CutoutPathParserInfo(
+                info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
+                info.getCutoutSpec(), rotation, info.getScale());
         return WmDisplayCutout.computeSafeInsets(
-                DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets),
+                DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo),
                 rotated ? mInitialDisplayHeight : mInitialDisplayWidth,
                 rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
     }
@@ -4102,8 +4107,15 @@
      * Callbacks when the given type of {@link WindowContainer} animation finished running in the
      * hierarchy.
      */
-    void onWindowAnimationFinished(int type) {
+    void onWindowAnimationFinished(@NonNull WindowContainer wc, int type) {
         if (type == ANIMATION_TYPE_APP_TRANSITION || type == ANIMATION_TYPE_RECENTS) {
+            // Unfreeze the insets state of the frozen target when the animation finished if exists.
+            final Task task = wc.asTask();
+            if (task != null) {
+                task.forAllWindows(w -> {
+                    w.clearFrozenInsetsState();
+                }, true /* traverseTopToBottom */);
+            }
             removeImeSurfaceImmediately();
         }
     }
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 398049f..267f677 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -74,7 +74,7 @@
     private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>();
 
     private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
-        if (w.isVisible()) {
+        if (w.isReadyToDispatchInsetsState()) {
             w.notifyInsetsChanged();
         }
     };
@@ -117,7 +117,8 @@
         final @InternalInsetsType int type = provider != null
                 ? provider.getSource().getType() : ITYPE_INVALID;
         return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(),
-                isAboveIme(target));
+                isAboveIme(target),
+                target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() : mState);
     }
 
     InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
@@ -132,7 +133,7 @@
         final @WindowingMode int windowingMode = token != null
                 ? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
         final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
-        return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token));
+        return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token), mState);
     }
 
     private boolean isAboveIme(WindowContainer target) {
@@ -180,9 +181,8 @@
      * @see #getInsetsForWindowMetrics
      */
     private InsetsState getInsetsForTarget(@InternalInsetsType int type,
-            @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) {
-        InsetsState state = mState;
-
+            @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme,
+            @NonNull InsetsState state) {
         if (type != ITYPE_INVALID) {
             state = new InsetsState(state);
             state.removeSource(type);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 03fca11..dd4ee877 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2684,6 +2684,14 @@
             @Nullable ArrayList<WindowContainer> sources) {
         final Task task = asTask();
         if (task != null && !enter && !task.isHomeOrRecentsRootTask()) {
+            if (AppTransition.isClosingTransitOld(transit)) {
+                // Freezes the insets state when the window is in app exiting transition, to
+                // ensure the exiting window won't receive unexpected insets changes from the
+                // next window.
+                task.forAllWindows(w -> {
+                    w.freezeInsetsState();
+                }, true /* traverseTopToBottom */);
+            }
             mDisplayContent.showImeScreenshot();
         }
         final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
@@ -2831,7 +2839,7 @@
         }
         mSurfaceAnimationSources.clear();
         if (mDisplayContent != null) {
-            mDisplayContent.onWindowAnimationFinished(type);
+            mDisplayContent.onWindowAnimationFinished(this, type);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9f3188b..9a7823e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -713,6 +713,12 @@
     private @Nullable InsetsSourceProvider mControllableInsetProvider;
     private final InsetsState mRequestedInsetsState = new InsetsState();
 
+    /**
+     * Freeze the insets state in some cases that not necessarily keeps up-to-date to the client.
+     * (e.g app exiting transition)
+     */
+    private InsetsState mFrozenInsetsState;
+
     @Nullable InsetsSourceProvider mPendingPositionChanged;
 
     private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
@@ -758,6 +764,33 @@
         }
     }
 
+    /**
+     * Set a freeze state for the window to ignore dispatching its insets state to the client.
+     *
+     * Used to keep the insets state for some use cases. (e.g. app exiting transition)
+     */
+    void freezeInsetsState() {
+        if (mFrozenInsetsState == null) {
+            mFrozenInsetsState = new InsetsState(getInsetsState(), true /* copySources */);
+        }
+    }
+
+    void clearFrozenInsetsState() {
+        mFrozenInsetsState = null;
+    }
+
+    InsetsState getFrozenInsetsState() {
+        return mFrozenInsetsState;
+    }
+
+    /**
+     * Check if the insets state of the window is ready to dispatch to the client when invoking
+     * {@link InsetsStateController#notifyInsetsChanged}.
+     */
+    boolean isReadyToDispatchInsetsState() {
+        return isVisible() && mFrozenInsetsState == null;
+    }
+
     void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
             @Rotation int rotation, boolean requested) {
         // Invisible windows and the wallpaper do not participate in the seamless rotation animation
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 6fabc58..dfa6083 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -210,7 +210,17 @@
     ErrorCode setUidReadTimeouts(const Control& control,
                                  const std::vector<android::os::incremental::PerUidReadTimeouts>&
                                          perUidReadTimeouts) const final {
-        return -ENOTSUP;
+        std::vector<incfs::UidReadTimeouts> timeouts;
+        timeouts.resize(perUidReadTimeouts.size());
+        for (int i = 0, size = perUidReadTimeouts.size(); i < size; ++i) {
+            auto&& timeout = timeouts[i];
+            const auto& perUidTimeout = perUidReadTimeouts[i];
+            timeout.uid = perUidTimeout.uid;
+            timeout.minTimeUs = perUidTimeout.minTimeUs;
+            timeout.minPendingTimeUs = perUidTimeout.minPendingTimeUs;
+            timeout.maxPendingTimeUs = perUidTimeout.maxPendingTimeUs;
+        }
+        return incfs::setUidReadTimeouts(control, timeouts);
     }
 };
 
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 091e688..5453de1 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -45,7 +45,6 @@
 import com.android.server.SystemService;
 import com.android.server.people.data.DataManager;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.function.Consumer;
@@ -156,6 +155,13 @@
     final IBinder mService = new IPeopleManager.Stub() {
 
         @Override
+        public ConversationChannel getConversation(
+                String packageName, int userId, String shortcutId) {
+            enforceSystemRootOrSystemUI(getContext(), "get conversation");
+            return mDataManager.getConversation(packageName, userId, shortcutId);
+        }
+
+        @Override
         public ParceledListSlice<ConversationChannel> getRecentConversations() {
             enforceSystemRootOrSystemUI(getContext(), "get recent conversations");
             return new ParceledListSlice<>(
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 7521415..9a9a171 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -222,33 +222,65 @@
                 mContext.getPackageName(), intentFilter, callingUserId);
     }
 
+    /**
+     * Returns a {@link ConversationChannel} with the associated {@code shortcutId} if existent.
+     * Otherwise, returns null.
+     */
+    @Nullable
+    public ConversationChannel getConversation(String packageName, int userId, String shortcutId) {
+        UserData userData = getUnlockedUserData(userId);
+        if (userData != null) {
+            PackageData packageData = userData.getPackageData(packageName);
+            // App may have been uninstalled.
+            if (packageData != null) {
+                return getConversationChannel(packageData, shortcutId);
+            }
+        }
+        return null;
+    }
+
+    @Nullable
+    private ConversationChannel getConversationChannel(PackageData packageData, String shortcutId) {
+        ConversationInfo conversationInfo = packageData.getConversationInfo(shortcutId);
+        if (conversationInfo == null) {
+            return null;
+        }
+        int userId = packageData.getUserId();
+        String packageName = packageData.getPackageName();
+        ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId);
+        if (shortcutInfo == null) {
+            return null;
+        }
+        int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
+        NotificationChannel parentChannel =
+                mNotificationManagerInternal.getNotificationChannel(packageName, uid,
+                        conversationInfo.getParentNotificationChannelId());
+        NotificationChannelGroup parentChannelGroup = null;
+        if (parentChannel != null) {
+            parentChannelGroup =
+                    mNotificationManagerInternal.getNotificationChannelGroup(packageName,
+                            uid, parentChannel.getId());
+        }
+        return new ConversationChannel(shortcutInfo, uid, parentChannel,
+                parentChannelGroup,
+                conversationInfo.getLastEventTimestamp(),
+                hasActiveNotifications(packageName, userId, shortcutId));
+    }
+
     /** Returns the cached non-customized recent conversations. */
     public List<ConversationChannel> getRecentConversations(@UserIdInt int callingUserId) {
         List<ConversationChannel> conversationChannels = new ArrayList<>();
         forPackagesInProfile(callingUserId, packageData -> {
-            String packageName = packageData.getPackageName();
-            int userId = packageData.getUserId();
             packageData.forAllConversations(conversationInfo -> {
                 if (!isCachedRecentConversation(conversationInfo)) {
                     return;
                 }
                 String shortcutId = conversationInfo.getShortcutId();
-                ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId);
-                int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
-                NotificationChannel parentChannel =
-                        mNotificationManagerInternal.getNotificationChannel(packageName, uid,
-                                conversationInfo.getParentNotificationChannelId());
-                if (shortcutInfo == null || parentChannel == null) {
+                ConversationChannel channel = getConversationChannel(packageData, shortcutId);
+                if (channel == null || channel.getParentNotificationChannel() == null) {
                     return;
                 }
-                NotificationChannelGroup parentChannelGroup =
-                        mNotificationManagerInternal.getNotificationChannelGroup(packageName,
-                                uid, parentChannel.getId());
-                conversationChannels.add(
-                        new ConversationChannel(shortcutInfo, uid, parentChannel,
-                                parentChannelGroup,
-                                conversationInfo.getLastEventTimestamp(),
-                                hasActiveNotifications(packageName, userId, shortcutId)));
+                conversationChannels.add(channel);
             });
         });
         return conversationChannels;
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 63330d5..161d316 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -515,6 +515,85 @@
     }
 
     @Test
+    public void testGetConversationReturnsCustomizedConversation() {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        mDataManager.addOrUpdateConversationInfo(shortcut);
+
+        NotificationListenerService listenerService =
+                mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+
+        listenerService.onNotificationPosted(mStatusBarNotification);
+        shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
+
+        assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+                TEST_SHORTCUT_ID)).isNotNull();
+
+        listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
+                mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+        assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+                TEST_SHORTCUT_ID)).isNotNull();
+    }
+
+    @Test
+    public void testGetConversation() {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+        assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+            TEST_SHORTCUT_ID)).isNull();
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        shortcut.setCached(ShortcutInfo.FLAG_PINNED);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
+        assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+            TEST_SHORTCUT_ID)).isNotNull();
+        assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+                TEST_SHORTCUT_ID + "1")).isNull();
+
+        NotificationListenerService listenerService =
+                mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+        listenerService.onNotificationPosted(mStatusBarNotification);
+
+        ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+                TEST_SHORTCUT_ID);
+        assertThat(result).isNotNull();
+        assertEquals(shortcut.getId(), result.getShortcutInfo().getId());
+        assertEquals(1, result.getShortcutInfo().getPersons().length);
+        assertEquals(CONTACT_URI, result.getShortcutInfo().getPersons()[0].getUri());
+        assertEquals(mParentNotificationChannel.getId(),
+                result.getParentNotificationChannel().getId());
+        assertEquals(mStatusBarNotification.getPostTime(), result.getLastEventTimestamp());
+        assertTrue(result.hasActiveNotifications());
+    }
+
+    @Test
+    public void testGetConversationGetsPersonsData() {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        shortcut.setCached(ShortcutInfo.FLAG_PINNED);
+        mDataManager.addOrUpdateConversationInfo(shortcut);
+
+        NotificationListenerService listenerService =
+                mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+        listenerService.onNotificationPosted(mStatusBarNotification);
+
+        ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+                TEST_SHORTCUT_ID);
+
+        verify(mShortcutServiceInternal).getShortcuts(
+                anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(),
+                mQueryFlagsCaptor.capture(), anyInt(), anyInt(), anyInt());
+        Integer queryFlags = mQueryFlagsCaptor.getValue();
+        assertThat(hasFlag(queryFlags, ShortcutQuery.FLAG_GET_PERSONS_DATA)).isTrue();
+    }
+
+    @Test
     public void testNotificationChannelCreated() {
         mDataManager.onUserUnlocked(USER_ID_PRIMARY);
         mDataManager.onUserUnlocked(USER_ID_SECONDARY);
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 533dc17..1d0b595 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -877,6 +877,22 @@
     }
 
     @Test
+    public void testQuiescentBoot_WakeKeyBeforeBootCompleted_AwakeAfterBootCompleted()
+            throws Exception {
+        when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
+        createService();
+        mService.systemReady(null);
+
+        mService.getBinderServiceInstance().wakeUp(mClock.now(),
+                PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name");
+
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+        assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+                DisplayPowerRequest.POLICY_BRIGHT);
+    }
+
+    @Test
     public void testIsAmbientDisplayAvailable_available() throws Exception {
         createService();
         when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 78dd4b8..4bea9a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -27,7 +27,6 @@
 import static android.os.Build.VERSION_CODES.Q;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.FLAG_PRIVATE;
-import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.DisplayCutout.fromBoundingRect;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -96,6 +95,7 @@
 import android.app.WindowConfiguration;
 import android.app.servertransaction.FixedRotationAdjustmentsItem;
 import android.content.res.Configuration;
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.metrics.LogMaker;
@@ -707,6 +707,7 @@
         // same width and height.
         final int displayWidth = dc.mInitialDisplayWidth;
         final int displayHeight = dc.mInitialDisplayHeight;
+        final float density = dc.mInitialDisplayDensity;
         final int cutoutWidth = 40;
         final int cutoutHeight = 10;
         final int left = (displayWidth - cutoutWidth) / 2;
@@ -714,9 +715,13 @@
         final int right = (displayWidth + cutoutWidth) / 2;
         final int bottom = cutoutHeight;
 
-        final Rect r1 = new Rect(left, top, right, bottom);
+        final Rect zeroRect = new Rect();
+        final Rect[] bounds = new Rect[]{zeroRect, new Rect(left, top, right, bottom), zeroRect,
+                zeroRect};
+        final DisplayCutout.CutoutPathParserInfo info = new DisplayCutout.CutoutPathParserInfo(
+                displayWidth, displayHeight, density, "", Surface.ROTATION_0, 1f);
         final DisplayCutout cutout = new WmDisplayCutout(
-                fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP), null)
+                DisplayCutout.constructDisplayCutout(bounds, Insets.NONE, info), null)
                         .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
 
         dc.mInitialDisplayCutout = cutout;
@@ -731,9 +736,12 @@
         // |             |      ---o
         // |             |      |
         // |             |      -------------
-        final Rect r = new Rect(top, left, bottom, right);
+        final Rect[] bounds90 = new Rect[]{new Rect(top, left, bottom, right), zeroRect, zeroRect,
+                zeroRect};
+        final DisplayCutout.CutoutPathParserInfo info90 = new DisplayCutout.CutoutPathParserInfo(
+                displayWidth, displayHeight, density, "", Surface.ROTATION_90, 1f);
         assertEquals(new WmDisplayCutout(
-                fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_LEFT), null)
+                        DisplayCutout.constructDisplayCutout(bounds90, Insets.NONE, info90), null)
                         .computeSafeInsets(displayHeight, displayWidth).getDisplayCutout(),
                 dc.getDisplayInfo().displayCutout);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index b0b8afd..df5b48a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -22,6 +22,8 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
@@ -978,6 +980,31 @@
         assertEquals(200, listener.mConfiguration.densityDpi);
     }
 
+    @Test
+    public void testFreezeInsetsStateWhenAppTransition() {
+        final Task stack = createTaskStackOnDisplay(mDisplayContent);
+        final Task task = createTaskInStack(stack, 0 /* userId */);
+        final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+        task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
+        spyOn(win);
+        doReturn(true).when(task).okToAnimate();
+        ArrayList<WindowContainer> sources = new ArrayList<>();
+        sources.add(activity);
+
+        // Simulate the task applying the exit transition, verify the main window of the task
+        // will be set the frozen insets state.
+        task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
+                false /* isVoiceInteraction */, sources);
+        verify(win).freezeInsetsState();
+
+        // Simulate the task transition finished, verify the frozen insets state of the window
+        // will be reset.
+        task.onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION,
+                task.mSurfaceAnimator.getAnimation());
+        verify(win).clearFrozenInsetsState();
+    }
+
     /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
     private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         private final int mLayer;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 263aa19..3231f8b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -810,4 +810,27 @@
                 WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
     }
+
+    @Test
+    public void testSetFreezeInsetsState() {
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        spyOn(app);
+        doReturn(true).when(app).isVisible();
+
+        // Set freezing the insets state to make the window ignore to dispatch insets changed.
+        final InsetsState expectedState = new InsetsState(app.getInsetsState(),
+                true /* copySources */);
+        app.freezeInsetsState();
+        assertEquals(expectedState, app.getFrozenInsetsState());
+        assertFalse(app.isReadyToDispatchInsetsState());
+        assertEquals(expectedState, app.getInsetsState());
+        mDisplayContent.getInsetsStateController().notifyInsetsChanged();
+        verify(app, never()).notifyInsetsChanged();
+
+        // Unfreeze the insets state to make the window can dispatch insets changed.
+        app.clearFrozenInsetsState();
+        assertTrue(app.isReadyToDispatchInsetsState());
+        mDisplayContent.getInsetsStateController().notifyInsetsChanged();
+        verify(app).notifyInsetsChanged();
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
index 39976a5..b2646f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
@@ -150,9 +150,9 @@
     @Test
     public void computeSafeInsets_waterfall() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                DisplayCutout.fromBoundsAndWaterfall(
+                DisplayCutout.constructDisplayCutout(
                         new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT},
-                        Insets.of(1, 2, 3, 4)),
+                        Insets.of(1, 2, 3, 4), null),
                 200, 400);
 
         assertEquals(new Rect(1, 2, 3, 4), cutout.getDisplayCutout().getSafeInsets());
@@ -161,9 +161,9 @@
     @Test
     public void computeSafeInsets_cutoutTop_greaterThan_waterfallTop() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                DisplayCutout.fromBoundsAndWaterfall(
+                DisplayCutout.constructDisplayCutout(
                         new Rect[] {ZERO_RECT, new Rect(80, 0, 120, 30), ZERO_RECT, ZERO_RECT},
-                        Insets.of(0, 20, 0, 0)),
+                        Insets.of(0, 20, 0, 0), null),
                 200, 400);
 
         assertEquals(new Rect(0, 30, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -172,9 +172,9 @@
     @Test
     public void computeSafeInsets_cutoutTop_lessThan_waterfallTop() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                DisplayCutout.fromBoundsAndWaterfall(
+                DisplayCutout.constructDisplayCutout(
                         new Rect[] {ZERO_RECT, new Rect(80, 0, 120, 30), ZERO_RECT, ZERO_RECT},
-                        Insets.of(0, 40, 0, 0)),
+                        Insets.of(0, 40, 0, 0), null),
                 200, 400);
 
         assertEquals(new Rect(0, 40, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -183,9 +183,9 @@
     @Test
     public void computeSafeInsets_cutoutLeft_greaterThan_waterfallLeft() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                DisplayCutout.fromBoundsAndWaterfall(
+                DisplayCutout.constructDisplayCutout(
                         new Rect[] {new Rect(0, 180, 30, 220), ZERO_RECT, ZERO_RECT, ZERO_RECT},
-                        Insets.of(20, 0, 0, 0)),
+                        Insets.of(20, 0, 0, 0), null),
                 200, 400);
 
         assertEquals(new Rect(30, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -194,9 +194,9 @@
     @Test
     public void computeSafeInsets_cutoutLeft_lessThan_waterfallLeft() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                DisplayCutout.fromBoundsAndWaterfall(
+                DisplayCutout.constructDisplayCutout(
                         new Rect[] {new Rect(0, 180, 30, 220), ZERO_RECT, ZERO_RECT, ZERO_RECT},
-                        Insets.of(40, 0, 0, 0)),
+                        Insets.of(40, 0, 0, 0), null),
                 200, 400);
 
         assertEquals(new Rect(40, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -205,9 +205,9 @@
     @Test
     public void computeSafeInsets_cutoutBottom_greaterThan_waterfallBottom() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                DisplayCutout.fromBoundsAndWaterfall(
+                DisplayCutout.constructDisplayCutout(
                         new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(80, 370, 120, 400)},
-                        Insets.of(0, 0, 0, 20)),
+                        Insets.of(0, 0, 0, 20), null),
                 200, 400);
 
         assertEquals(new Rect(0, 0, 0, 30), cutout.getDisplayCutout().getSafeInsets());
@@ -216,9 +216,9 @@
     @Test
     public void computeSafeInsets_cutoutBottom_lessThan_waterfallBottom() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                DisplayCutout.fromBoundsAndWaterfall(
+                DisplayCutout.constructDisplayCutout(
                         new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(80, 370, 120, 400)},
-                        Insets.of(0, 0, 0, 40)),
+                        Insets.of(0, 0, 0, 40), null),
                 200, 400);
 
         assertEquals(new Rect(0, 0, 0, 40), cutout.getDisplayCutout().getSafeInsets());
@@ -227,9 +227,9 @@
     @Test
     public void computeSafeInsets_cutoutRight_greaterThan_waterfallRight() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                DisplayCutout.fromBoundsAndWaterfall(
+                DisplayCutout.constructDisplayCutout(
                         new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(170, 180, 200, 220), ZERO_RECT},
-                        Insets.of(0, 0, 20, 0)),
+                        Insets.of(0, 0, 20, 0), null),
                 200, 400);
 
         assertEquals(new Rect(0, 0, 30, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -238,9 +238,9 @@
     @Test
     public void computeSafeInsets_cutoutRight_lessThan_waterfallRight() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                DisplayCutout.fromBoundsAndWaterfall(
+                DisplayCutout.constructDisplayCutout(
                         new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(170, 180, 200, 220), ZERO_RECT},
-                        Insets.of(0, 0, 40, 0)),
+                        Insets.of(0, 0, 40, 0), null),
                 200, 400);
 
         assertEquals(new Rect(0, 0, 40, 0), cutout.getDisplayCutout().getSafeInsets());