Merge "v4 signing schema parsing and verification."
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 661f32f..c458d11 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -670,7 +670,7 @@
     private void startApp(int userId, String packageName) throws RemoteException {
         final Context context = InstrumentationRegistry.getContext();
         final WaitResult result = ActivityTaskManager.getService().startActivityAndWait(null,
-                context.getPackageName(),
+                context.getPackageName(), context.getFeatureId(),
                 context.getPackageManager().getLaunchIntentForPackage(packageName), null, null,
                 null, 0, 0, null, null, userId);
         attestTrue("User " + userId + " failed to start " + packageName,
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
index d339afa..9c3bd81 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
@@ -129,7 +129,8 @@
      * @param label a label indicating what the blob is, that can be surfaced to the user.
      * @param expiryTimeMillis the time in secs after which the blob should be invalidated and not
      *                         allowed to be accessed by any other app,
-     *                         in {@link System#currentTimeMillis()} timebase.
+     *                         in {@link System#currentTimeMillis()} timebase or {@code 0} to
+     *                         indicate that there is no expiry time associated with this blob.
      * @param tag an opaque {@link String} associated with the blob. The length of the tag
      *            cannot be more than 128 characters.
      *
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 821305e..f53f1f1 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -266,6 +266,9 @@
      * <p> This lease information is persisted and calling this more than once will result in
      * latest lease overriding any previous lease.
      *
+     * <p> When an app acquires a lease on a blob, the System will try to keep this
+     * blob around but note that it can still be deleted if it was requested by the user.
+     *
      * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
      *                   acquire a lease for.
      * @param descriptionResId the resource id for a short description string that can be surfaced
@@ -307,7 +310,7 @@
      * Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the
      * system that the caller wants the blob to be kept around.
      *
-     * <p> This is variant of {@link #acquireLease(BlobHandle, int, long)} taking a
+     * <p> This is a variant of {@link #acquireLease(BlobHandle, int, long)} taking a
      * {@link CharSequence} for {@code description}. It is highly recommended that callers only
      * use this when a valid resource ID for {@code description} could not be provided. Otherwise,
      * apps should prefer using {@link #acquireLease(BlobHandle, int)} which will allow
@@ -319,6 +322,9 @@
      * <p> This lease information is persisted and calling this more than once will result in
      * latest lease overriding any previous lease.
      *
+     * <p> When an app acquires a lease on a blob, the System will try to keep this
+     * blob around but note that it can still be deleted if it was requested by the user.
+     *
      * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
      *                   acquire a lease for.
      * @param description a short description string that can be surfaced
@@ -371,6 +377,9 @@
      * <p> This lease information is persisted and calling this more than once will result in
      * latest lease overriding any previous lease.
      *
+     * <p> When an app acquires a lease on a blob, the System will try to keep this
+     * blob around but note that it can still be deleted if it was requested by the user.
+     *
      * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
      *                   acquire a lease for.
      * @param descriptionResId the resource id for a short description string that can be surfaced
@@ -395,7 +404,7 @@
      * Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the
      * system that the caller wants the blob to be kept around.
      *
-     * <p> This is variant of {@link #acquireLease(BlobHandle, int)} taking a {@link CharSequence}
+     * <p> This is a variant of {@link #acquireLease(BlobHandle, int)} taking a {@link CharSequence}
      * for {@code description}. It is highly recommended that callers only use this when a valid
      * resource ID for {@code description} could not be provided. Otherwise, apps should prefer
      * using {@link #acquireLease(BlobHandle, int)} which will allow {@code description} to be
@@ -412,6 +421,9 @@
      * <p> This lease information is persisted and calling this more than once will result in
      * latest lease overriding any previous lease.
      *
+     * <p> When an app acquires a lease on a blob, the System will try to keep this
+     * blob around but note that it can still be deleted if it was requested by the user.
+     *
      * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
      *                   acquire a lease for.
      * @param description a short description string that can be surfaced
diff --git a/apex/extservices/testing/Android.bp b/apex/extservices/testing/Android.bp
new file mode 100644
index 0000000..88a4724
--- /dev/null
+++ b/apex/extservices/testing/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+apex_test {
+    name: "test_com.android.extservices",
+    visibility: [
+        "//system/apex/tests",
+    ],
+    defaults: ["com.android.extservices-defaults"],
+    manifest: "test_manifest.json",
+    file_contexts: ":com.android.extservices-file_contexts",
+    // Test APEX, should never be installed
+    installable: false,
+}
diff --git a/apex/extservices/testing/test_manifest.json b/apex/extservices/testing/test_manifest.json
new file mode 100644
index 0000000..23a50e3
--- /dev/null
+++ b/apex/extservices/testing/test_manifest.json
@@ -0,0 +1,4 @@
+{
+  "name": "com.android.extservices",
+  "version": 2147483647
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index df44a07..f1bfa04 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -663,7 +663,7 @@
 
                 if (app.lastRestrictAttemptElapsedTime > app.lastUsedByUserElapsedTime
                         && elapsedTimeAdjusted - app.lastUsedByUserElapsedTime
-                        >= mInjector.getRestrictedBucketDelayMs()) {
+                        >= mInjector.getAutoRestrictedBucketDelayMs()) {
                     newBucket = STANDBY_BUCKET_RESTRICTED;
                     reason = app.lastRestrictReason;
                     if (DEBUG) {
@@ -1219,7 +1219,7 @@
                     }
                 } else {
                     final long timeUntilRestrictPossibleMs = app.lastUsedByUserElapsedTime
-                            + mInjector.getRestrictedBucketDelayMs() - elapsedRealtime;
+                            + mInjector.getAutoRestrictedBucketDelayMs() - elapsedRealtime;
                     if (timeUntilRestrictPossibleMs > 0) {
                         Slog.w(TAG, "Tried to restrict recently used app: " + packageName
                                 + " due to " + reason);
@@ -1566,10 +1566,9 @@
         int mBootPhase;
         /**
          * The minimum amount of time required since the last user interaction before an app can be
-         * placed in the RESTRICTED bucket.
+         * automatically placed in the RESTRICTED bucket.
          */
-        // TODO: make configurable via DeviceConfig
-        private long mRestrictedBucketDelayMs = ONE_DAY;
+        long mAutoRestrictedBucketDelayMs = ONE_DAY;
 
         Injector(Context context, Looper looper) {
             mContext = context;
@@ -1598,7 +1597,7 @@
                 final ActivityManager activityManager =
                         (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
                 if (activityManager.isLowRamDevice() || ActivityManager.isSmallBatteryDevice()) {
-                    mRestrictedBucketDelayMs = 12 * ONE_HOUR;
+                    mAutoRestrictedBucketDelayMs = 12 * ONE_HOUR;
                 }
             }
             mBootPhase = phase;
@@ -1639,8 +1638,13 @@
             return Environment.getDataSystemDirectory();
         }
 
-        long getRestrictedBucketDelayMs() {
-            return mRestrictedBucketDelayMs;
+        /**
+         * Return the minimum amount of time that must have passed since the last user usage before
+         * an app can be automatically put into the
+         * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket.
+         */
+        long getAutoRestrictedBucketDelayMs() {
+            return mAutoRestrictedBucketDelayMs;
         }
 
         void noteEvent(int event, String packageName, int uid) throws RemoteException {
@@ -1816,6 +1820,8 @@
                 "system_interaction_duration";
         private static final String KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION =
                 "initial_foreground_service_start_duration";
+        private static final String KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS =
+                "auto_restricted_bucket_delay_ms";
         public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR;
         public static final long DEFAULT_NOTIFICATION_TIMEOUT = 12 * ONE_HOUR;
         public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = 2 * ONE_HOUR;
@@ -1826,6 +1832,7 @@
         public static final long DEFAULT_EXEMPTED_SYNC_START_TIMEOUT = 10 * ONE_MINUTE;
         public static final long DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT = 10 * ONE_MINUTE;
         public static final long DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT = 30 * ONE_MINUTE;
+        public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS = ONE_DAY;
 
         private final KeyValueListParser mParser = new KeyValueListParser(',');
 
@@ -1925,6 +1932,12 @@
                         KEY_INITIAL_FOREGROUND_SERVICE_START_HOLD_DURATION,
                         COMPRESS_TIME ? ONE_MINUTE :
                                 DEFAULT_INITIAL_FOREGROUND_SERVICE_START_TIMEOUT);
+
+                mInjector.mAutoRestrictedBucketDelayMs = Math.max(
+                        COMPRESS_TIME ? ONE_MINUTE : 2 * ONE_HOUR,
+                        mParser.getDurationMillis(KEY_AUTO_RESTRICTED_BUCKET_DELAY_MS,
+                                COMPRESS_TIME
+                                        ? ONE_MINUTE : DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS));
             }
 
             // Check if app_idle_enabled has changed. Do this after getting the rest of the settings
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index 23ae8af..821dd9e 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -19,21 +19,6 @@
         ":updatable-media-srcs",
     ],
 
-    aidl: {
-        export_include_dirs: [
-            "java",
-        ],
-
-        // It would be great if we don't need to add include_dirs for public
-        // parcelable classes. Find a better way.
-        include_dirs: [
-            // To refer:
-            // android.os.Bundle
-            // android.os.ResultReceiver
-            "frameworks/base/core/java",
-        ],
-    },
-
     permitted_packages: [
         "android.media",
     ],
@@ -46,13 +31,9 @@
 
     installable: true,
 
-    // TODO: build against stable API surface. Use core_platform for now to avoid
-    // link-check failure with exoplayer building against "current".
-    sdk_version: "core_platform",
+    sdk_version: "module_current",
     libs: [
-        // The order matters. android_system_* library should come later.
         "framework_media_annotation",
-        "android_system_stubs_current",
     ],
 
     static_libs: [
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index 96110e1..3eed26b 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -811,11 +811,14 @@
 
     private static MediaFormat toMediaFormat(Format format) {
 
-        // TODO: Add if (value != Format.NO_VALUE);
-
         MediaFormat result = new MediaFormat();
-        result.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate);
-        result.setInteger(MediaFormat.KEY_CHANNEL_COUNT, format.channelCount);
+        if (format.bitrate != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate);
+        }
+        if (format.channelCount != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_CHANNEL_COUNT, format.channelCount);
+        }
+
         if (format.colorInfo != null) {
             result.setInteger(MediaFormat.KEY_COLOR_TRANSFER, format.colorInfo.colorTransfer);
             result.setInteger(MediaFormat.KEY_COLOR_RANGE, format.colorInfo.colorRange);
@@ -826,25 +829,44 @@
                         ByteBuffer.wrap(format.colorInfo.hdrStaticInfo));
             }
         }
-        result.setString(MediaFormat.KEY_MIME, format.sampleMimeType);
-        result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate);
-        result.setInteger(MediaFormat.KEY_WIDTH, format.width);
-        result.setInteger(MediaFormat.KEY_HEIGHT, format.height);
+
+        if (format.sampleMimeType != null) {
+            result.setString(MediaFormat.KEY_MIME, format.sampleMimeType);
+        }
+        if (format.codecs != null) {
+            result.setString(MediaFormat.KEY_CODECS_STRING, format.codecs);
+        }
+        if (format.frameRate != Format.NO_VALUE) {
+            result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate);
+        }
+        if (format.width != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_WIDTH, format.width);
+        }
+        if (format.height != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_HEIGHT, format.height);
+        }
         List<byte[]> initData = format.initializationData;
         if (initData != null) {
             for (int i = 0; i < initData.size(); i++) {
                 result.setByteBuffer("csd-" + i, ByteBuffer.wrap(initData.get(i)));
             }
         }
-        result.setString(MediaFormat.KEY_LANGUAGE, format.language);
-        result.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
-        result.setInteger(MediaFormat.KEY_PCM_ENCODING, format.pcmEncoding);
-        result.setInteger(MediaFormat.KEY_ROTATION, format.rotationDegrees);
-        result.setInteger(MediaFormat.KEY_SAMPLE_RATE, format.sampleRate);
-
+        if (format.language != null) {
+            result.setString(MediaFormat.KEY_LANGUAGE, format.language);
+        }
+        if (format.maxInputSize != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
+        }
+        if (format.pcmEncoding != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_PCM_ENCODING, format.pcmEncoding);
+        }
+        if (format.rotationDegrees != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_ROTATION, format.rotationDegrees);
+        }
+        if (format.sampleRate != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_SAMPLE_RATE, format.sampleRate);
+        }
         int selectionFlags = format.selectionFlags;
-        // We avoid setting selection flags in the MediaFormat, unless explicitly signaled by the
-        // extractor.
         if ((selectionFlags & C.SELECTION_FLAG_AUTOSELECT) != 0) {
             result.setInteger(MediaFormat.KEY_IS_AUTOSELECT, 1);
         }
@@ -854,28 +876,29 @@
         if ((selectionFlags & C.SELECTION_FLAG_FORCED) != 0) {
             result.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, 1);
         }
+        if (format.encoderDelay != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_ENCODER_DELAY, format.encoderDelay);
+        }
+        if (format.encoderPadding != Format.NO_VALUE) {
+            result.setInteger(MediaFormat.KEY_ENCODER_PADDING, format.encoderPadding);
+        }
+        // TODO: Implement float to fraction conversion.
+        // if (format.pixelWidthHeightRatio != Format.NO_VALUE) {
+        //     result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_WIDTH, );
+        //     result.setInteger(MediaFormat.KEY_PIXEL_ASPECT_RATIO_HEIGHT, );
+        // }
 
         // LACK OF SUPPORT FOR:
         //    format.accessibilityChannel;
-        //    format.codecs;
         //    format.containerMimeType;
-        //    format.drmInitData;
-        //    format.encoderDelay;
-        //    format.encoderPadding;
         //    format.id;
         //    format.metadata;
-        //    format.pixelWidthHeightRatio;
         //    format.roleFlags;
         //    format.stereoMode;
         //    format.subsampleOffsetUs;
         return result;
     }
 
-    private static int toFrameworkFlags(int flags) {
-        // TODO: Implement.
-        return 0;
-    }
-
     private static DrmInitData toFrameworkDrmInitData(
             com.google.android.exoplayer2.drm.DrmInitData drmInitData) {
         // TODO: Implement.
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index 126fa00..6d96200 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -26,13 +26,7 @@
     srcs: [
         ":framework-permission-sources",
     ],
-    // TODO(b/146758669): Use "system_current" after nullability annotations are system APIs.
-    sdk_version: "core_current",
-    libs: [
-        "framework-annotations-lib",
-        // TODO(b/146758669): Remove this line after nullability annotations are system APIs.
-        "android_system_stubs_current",
-    ],
+    sdk_version: "module_current",
     apex_available: [
         "com.android.permission",
         "test_com.android.permission",
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
index 4172e95..8d66431 100644
--- a/apex/permission/service/Android.bp
+++ b/apex/permission/service/Android.bp
@@ -24,12 +24,9 @@
     srcs: [
         ":service-permission-sources",
     ],
-    // TODO(b/146758669): Use "system_current" after nullability annotations are system APIs.
-    sdk_version: "core_current",
+    sdk_version: "module_current",
     libs: [
         "framework-annotations-lib",
-        // TODO(b/146758669): Remove this line after nullability annotations are system APIs.
-        "android_system_stubs_current",
         "framework-permission",
     ],
     apex_available: [
diff --git a/apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java b/apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java
index 3d95533..8dc9123 100644
--- a/apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java
+++ b/apex/statsd/framework/java/android/os/StatsFrameworkInitializer.java
@@ -17,6 +17,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
 import android.app.StatsManager;
 import android.app.SystemServiceRegistry;
 import android.content.Context;
@@ -24,10 +25,9 @@
 /**
  * Class for performing registration for all stats services
  *
- * TODO(b/148225705) Change to @SystemApi(client=MODULE_LIBRARIES) when the build system is ready.
  * @hide
  */
-@SystemApi
+@SystemApi(client = Client.MODULE_LIBRARIES)
 public class StatsFrameworkInitializer {
     private StatsFrameworkInitializer() {
     }
diff --git a/api/current.txt b/api/current.txt
index 3cde15c..89faaa3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -91,6 +91,7 @@
     field public static final String INTERACT_ACROSS_PROFILES = "android.permission.INTERACT_ACROSS_PROFILES";
     field public static final String INTERNET = "android.permission.INTERNET";
     field public static final String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
+    field public static final String LOADER_USAGE_STATS = "android.permission.LOADER_USAGE_STATS";
     field public static final String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
     field public static final String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
     field public static final String MANAGE_EXTERNAL_STORAGE = "android.permission.MANAGE_EXTERNAL_STORAGE";
@@ -26881,7 +26882,8 @@
     method public void onDiscoveryPreferenceChanged(@NonNull android.media.RouteDiscoveryPreference);
     method public abstract void onReleaseSession(@NonNull String);
     method public abstract void onSelectRoute(@NonNull String, @NonNull String);
-    method public abstract void onSetVolume(@NonNull String, int);
+    method public abstract void onSetRouteVolume(@NonNull String, int);
+    method public abstract void onSetSessionVolume(@NonNull String, int);
     method public abstract void onTransferToRoute(@NonNull String, @NonNull String);
     field public static final long REQUEST_ID_UNKNOWN = 0L; // 0x0L
     field public static final String SERVICE_INTERFACE = "android.media.MediaRoute2ProviderService";
@@ -27042,9 +27044,13 @@
     method @NonNull public java.util.List<android.media.MediaRoute2Info> getSelectableRoutes();
     method @NonNull public java.util.List<android.media.MediaRoute2Info> getSelectedRoutes();
     method @NonNull public java.util.List<android.media.MediaRoute2Info> getTransferrableRoutes();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
     method public boolean isReleased();
     method public void release();
     method public void selectRoute(@NonNull android.media.MediaRoute2Info);
+    method public void setVolume(int);
     method public void transferToRoute(@NonNull android.media.MediaRoute2Info);
   }
 
@@ -27439,6 +27445,9 @@
     method @NonNull public java.util.List<java.lang.String> getSelectableRoutes();
     method @NonNull public java.util.List<java.lang.String> getSelectedRoutes();
     method @NonNull public java.util.List<java.lang.String> getTransferrableRoutes();
+    method public int getVolume();
+    method public int getVolumeHandling();
+    method public int getVolumeMax();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.media.RoutingSessionInfo> CREATOR;
   }
@@ -27460,6 +27469,9 @@
     method @NonNull public android.media.RoutingSessionInfo.Builder removeSelectedRoute(@NonNull String);
     method @NonNull public android.media.RoutingSessionInfo.Builder removeTransferrableRoute(@NonNull String);
     method @NonNull public android.media.RoutingSessionInfo.Builder setControlHints(@Nullable android.os.Bundle);
+    method @NonNull public android.media.RoutingSessionInfo.Builder setVolume(int);
+    method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeHandling(int);
+    method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeMax(int);
   }
 
   public final class Session2Command implements android.os.Parcelable {
@@ -29788,10 +29800,12 @@
   }
 
   public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable {
-    ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.os.PersistableBundle);
+    ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
     method public int describeContents();
     method public int getDetectionMethod();
+    method @NonNull public android.net.LinkProperties getLinkProperties();
     method @NonNull public android.net.Network getNetwork();
+    method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
     method public long getReportTimestamp();
     method @NonNull public android.os.PersistableBundle getStallDetails();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -40320,6 +40334,7 @@
     field public static final String VOLUME_NAME = "volume_name";
     field public static final String WIDTH = "width";
     field public static final String WRITER = "writer";
+    field public static final String XMP = "xmp";
     field public static final String YEAR = "year";
   }
 
@@ -55208,7 +55223,7 @@
   }
 
   public interface WindowInsetsController {
-    method public void controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
+    method @NonNull public android.os.CancellationSignal controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
     method public int getSystemBarsAppearance();
     method public int getSystemBarsBehavior();
     method public void hide(int);
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 59aa145..7b66f73 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -1,4 +1,14 @@
 // Signature format: 2.0
+package android.annotation {
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD}) public @interface NonNull {
+  }
+
+  @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD}) public @interface Nullable {
+  }
+
+}
+
 package android.net {
 
   public final class TetheredClient implements android.os.Parcelable {
@@ -120,6 +130,30 @@
 
 }
 
+package android.os {
+
+  public class StatsFrameworkInitializer {
+    method public static void registerServiceWrappers();
+    method public static void setStatsServiceManager(@NonNull android.os.StatsServiceManager);
+  }
+
+  public class StatsServiceManager {
+    method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
+    method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
+    method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsdServiceRegisterer();
+  }
+
+  public static class StatsServiceManager.ServiceNotFoundException extends java.lang.Exception {
+    ctor public StatsServiceManager.ServiceNotFoundException(@NonNull String);
+  }
+
+  public static final class StatsServiceManager.ServiceRegisterer {
+    method @Nullable public android.os.IBinder get();
+    method @Nullable public android.os.IBinder getOrThrow() throws android.os.StatsServiceManager.ServiceNotFoundException;
+  }
+
+}
+
 package android.os.ext.test {
 
   @Deprecated public class Test {
diff --git a/api/system-current.txt b/api/system-current.txt
index 92307ba..838d23f 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -392,6 +392,7 @@
     field public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground";
     field public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles";
     field public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
+    field public static final String OPSTR_LOADER_USAGE_STATS = "android:loader_usage_stats";
     field public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage";
     field public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
     field public static final String OPSTR_MUTE_MICROPHONE = "android:mute_microphone";
@@ -8899,26 +8900,6 @@
     field public static final int TUPLE_VALUE_TYPE = 7; // 0x7
   }
 
-  public class StatsFrameworkInitializer {
-    method public static void registerServiceWrappers();
-    method public static void setStatsServiceManager(@NonNull android.os.StatsServiceManager);
-  }
-
-  public class StatsServiceManager {
-    method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
-    method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
-    method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsdServiceRegisterer();
-  }
-
-  public static class StatsServiceManager.ServiceNotFoundException extends java.lang.Exception {
-    ctor public StatsServiceManager.ServiceNotFoundException(@NonNull String);
-  }
-
-  public static final class StatsServiceManager.ServiceRegisterer {
-    method @Nullable public android.os.IBinder get();
-    method @Nullable public android.os.IBinder getOrThrow() throws android.os.StatsServiceManager.ServiceNotFoundException;
-  }
-
   public class SystemConfigManager {
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps();
     method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
@@ -12645,20 +12626,6 @@
     method public static final void setSmsFilterSettings(android.content.Context, android.telecom.PhoneAccountHandle, android.telephony.VisualVoicemailSmsFilterSettings);
   }
 
-  public final class WapPushManagerConnector {
-    ctor public WapPushManagerConnector(@NonNull android.content.Context);
-    method public boolean bindToWapPushManagerService();
-    method @Nullable public String getConnectedWapPushManagerServicePackage();
-    method public int processMessage(@NonNull String, @NonNull String, @NonNull android.content.Intent);
-    method public void unbindWapPushManagerService();
-    field public static final int RESULT_APP_QUERY_FAILED = 2; // 0x2
-    field public static final int RESULT_EXCEPTION_CAUGHT = 16; // 0x10
-    field public static final int RESULT_FURTHER_PROCESSING = 32768; // 0x8000
-    field public static final int RESULT_INVALID_RECEIVER_NAME = 8; // 0x8
-    field public static final int RESULT_MESSAGE_HANDLED = 1; // 0x1
-    field public static final int RESULT_SIGNATURE_NO_MATCH = 4; // 0x4
-  }
-
 }
 
 package android.telephony.cdma {
diff --git a/api/test-current.txt b/api/test-current.txt
index 225f3dc..7e8eb0c 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3257,6 +3257,7 @@
     method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent);
     method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId);
     method public void onDataRemovalRequest(@NonNull android.view.contentcapture.DataRemovalRequest);
+    method public void onDataShareRequest(@NonNull android.view.contentcapture.DataShareRequest, @NonNull android.service.contentcapture.DataShareCallback);
     method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId);
     method public void onDisconnected();
     method public final void setContentCaptureConditions(@NonNull String, @Nullable java.util.Set<android.view.contentcapture.ContentCaptureCondition>);
@@ -3265,6 +3266,16 @@
     field public static final String SERVICE_META_DATA = "android.content_capture";
   }
 
+  public interface DataShareCallback {
+    method public void onAccept(@NonNull java.util.concurrent.Executor, @NonNull android.service.contentcapture.DataShareReadAdapter);
+    method public void onReject();
+  }
+
+  public interface DataShareReadAdapter {
+    method public void onError(int);
+    method public void onStart(@NonNull android.os.ParcelFileDescriptor);
+  }
+
   public final class SnapshotData implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public android.app.assist.AssistContent getAssistContent();
diff --git a/cmds/bootanimation/Android.bp b/cmds/bootanimation/Android.bp
index 3e5877b..befb67b 100644
--- a/cmds/bootanimation/Android.bp
+++ b/cmds/bootanimation/Android.bp
@@ -41,6 +41,10 @@
     ],
 
     init_rc: ["bootanim.rc"],
+
+    cflags: [
+        "-Wno-deprecated-declarations",
+    ],
 }
 
 // libbootanimation
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp
index c5cf980..c441709 100644
--- a/cmds/idmap2/idmap2/Lookup.cpp
+++ b/cmds/idmap2/idmap2/Lookup.cpp
@@ -85,11 +85,42 @@
   return Error("failed to obtain resource id for %s", res.c_str());
 }
 
-Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) {
+void PrintValue(AssetManager2* const am, const Res_value& value, const ApkAssetsCookie& cookie,
+                std::string* const out) {
+  switch (value.dataType) {
+    case Res_value::TYPE_INT_DEC:
+      out->append(StringPrintf("%d", value.data));
+      break;
+    case Res_value::TYPE_INT_HEX:
+      out->append(StringPrintf("0x%08x", value.data));
+      break;
+    case Res_value::TYPE_INT_BOOLEAN:
+      out->append(value.data != 0 ? "true" : "false");
+      break;
+    case Res_value::TYPE_STRING: {
+      const ResStringPool* pool = am->GetStringPoolForCookie(cookie);
+      out->append("\"");
+      size_t len;
+      if (pool->isUTF8()) {
+        const char* str = pool->string8At(value.data, &len);
+        out->append(str, len);
+      } else {
+        const char16_t* str16 = pool->stringAt(value.data, &len);
+        out->append(Utf16ToUtf8(StringPiece16(str16, len)));
+      }
+      out->append("\"");
+    } break;
+    default:
+      out->append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
+      break;
+  }
+}
+
+Result<std::string> WARN_UNUSED GetValue(AssetManager2* const am, ResourceId resid) {
   Res_value value;
   ResTable_config config;
   uint32_t flags;
-  ApkAssetsCookie cookie = am.GetResource(resid, false, 0, &value, &config, &flags);
+  ApkAssetsCookie cookie = am->GetResource(resid, true, 0, &value, &config, &flags);
   if (cookie == kInvalidCookie) {
     return Error("no resource 0x%08x in asset manager", resid);
   }
@@ -104,31 +135,37 @@
   out.append(config.toString().c_str());
   out.append("' value=");
 
-  switch (value.dataType) {
-    case Res_value::TYPE_INT_DEC:
-      out.append(StringPrintf("%d", value.data));
-      break;
-    case Res_value::TYPE_INT_HEX:
-      out.append(StringPrintf("0x%08x", value.data));
-      break;
-    case Res_value::TYPE_INT_BOOLEAN:
-      out.append(value.data != 0 ? "true" : "false");
-      break;
-    case Res_value::TYPE_STRING: {
-      const ResStringPool* pool = am.GetStringPoolForCookie(cookie);
-      size_t len;
-      if (pool->isUTF8()) {
-        const char* str = pool->string8At(value.data, &len);
-        out.append(str, len);
-      } else {
-        const char16_t* str16 = pool->stringAt(value.data, &len);
-        out += Utf16ToUtf8(StringPiece16(str16, len));
-      }
-    } break;
-    default:
+  if (value.dataType == Res_value::TYPE_REFERENCE) {
+    const android::ResolvedBag* bag = am->GetBag(static_cast<uint32_t>(value.data));
+    if (bag == nullptr) {
       out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data));
-      break;
+      return out;
+    }
+    out.append("[");
+    Res_value bag_val;
+    ResTable_config selected_config;
+    uint32_t flags;
+    uint32_t ref;
+    ApkAssetsCookie bag_cookie;
+    for (size_t i = 0; i < bag->entry_count; ++i) {
+      const android::ResolvedBag::Entry& entry = bag->entries[i];
+      bag_val = entry.value;
+      bag_cookie = am->ResolveReference(entry.cookie, &bag_val, &selected_config, &flags, &ref);
+      if (bag_cookie == kInvalidCookie) {
+        out.append(
+            StringPrintf("Error: dataType=0x%02x data=0x%08x", bag_val.dataType, bag_val.data));
+        continue;
+      }
+      PrintValue(am, bag_val, bag_cookie, &out);
+      if (i != bag->entry_count - 1) {
+        out.append(", ");
+      }
+    }
+    out.append("]");
+  } else {
+    PrintValue(am, value, cookie, &out);
   }
+
   return out;
 }
 
@@ -212,7 +249,7 @@
     return Error(resid.GetError(), "failed to parse resource ID");
   }
 
-  const Result<std::string> value = GetValue(am, *resid);
+  const Result<std::string> value = GetValue(&am, *resid);
   if (!value) {
     return Error(value.GetError(), "resource 0x%08x not found", *resid);
   }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 23f9f22..1bc235a 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -396,7 +396,7 @@
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10071
+    // Next: 10074
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -471,6 +471,12 @@
         GraphicsStats graphics_stats = 10068;
         RuntimeAppOpAccess runtime_app_op_access = 10069 [(module) = "framework"];
         IonHeapSize ion_heap_size = 10070 [(module) = "framework"];
+        PackageNotificationPreferences package_notification_preferences =
+                10071 [(module) = "framework"];
+        PackageNotificationChannelPreferences package_notification_channel_preferences =
+                10072 [(module) = "framework"];
+        PackageNotificationChannelGroupPreferences package_notification_channel_group_preferences =
+                10073 [(module) = "framework"];
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -3475,27 +3481,25 @@
     // A small system-assigned identifier for the notification.
     // Locally probably-unique, but expect collisions across users and/or days.
     optional int32 instance_id = 4;
-    // The app-assigned notification ID and tag
-    optional int32 notification_id = 5;
-    optional string notification_tag = 6;
-    optional string channel_id = 7;  // App-assigned channel ID
+    optional int32 notification_id_hash = 5;  // Small hash of the app-assigned notif ID + tag
+    optional int32 channel_id_hash = 6;  // Small hash of app-assigned channel ID
 
     // Grouping information
-    optional string group_id = 8;  // Group the notification currently belongs to
-    optional int32 group_instance_id = 9;  // Instance_id of the group-summary notification
-    optional bool is_group_summary = 10;  // Tags the group-summary notification
+    optional int32 group_id_hash = 7;  // Small hash of the group ID of the notification
+    optional int32 group_instance_id = 8;  // Instance_id of the group-summary notification
+    optional bool is_group_summary = 9;  // Tags the group-summary notification
 
     // Attributes
-    optional string category = 11;   // App-assigned notification category (API-defined strings)
-    optional int32 style = 12;       // App-assigned notification style
-    optional int32 num_people = 13;  // Number of Person records attached to the notification
+    optional string category = 10;   // App-assigned notification category (API-defined strings)
+    optional int32 style = 11;       // App-assigned notification style
+    optional int32 num_people = 12;  // Number of Person records attached to the notification
 
     // Ordering, importance and interruptiveness
 
-    optional int32 position = 14;    // Position in NotificationManager's list
+    optional int32 position = 13;    // Position in NotificationManager's list
 
-    optional android.stats.sysui.NotificationImportance importance = 15;
-    optional int32 alerting = 16;    // Bitfield, 1=buzz 2=beep 4=blink
+    optional android.stats.sysui.NotificationImportance importance = 14;
+    optional int32 alerting = 15;    // Bitfield, 1=buzz 2=beep 4=blink
 
     enum NotificationImportanceExplanation {
         IMPORTANCE_EXPLANATION_UNKNOWN = 0;
@@ -3507,12 +3511,12 @@
         IMPORTANCE_EXPLANATION_APP_PRE_CHANNELS = 5;
     }
 
-    optional NotificationImportanceExplanation importance_source = 17;
-    optional android.stats.sysui.NotificationImportance importance_initial = 18;
-    optional NotificationImportanceExplanation importance_initial_source = 19;
-    optional android.stats.sysui.NotificationImportance importance_asst = 20;
-    optional int32 assistant_hash = 21;
-    optional float assistant_ranking_score = 22;
+    optional NotificationImportanceExplanation importance_source = 16;
+    optional android.stats.sysui.NotificationImportance importance_initial = 17;
+    optional NotificationImportanceExplanation importance_initial_source = 18;
+    optional android.stats.sysui.NotificationImportance importance_asst = 19;
+    optional int32 assistant_hash = 20;
+    optional float assistant_ranking_score = 21;
 }
 
 message Notification {
@@ -5469,6 +5473,65 @@
     optional NotificationRemoteViewsProto notification_remote_views = 1;
 }
 
+/**
+ * Atom that contains a list of a package's preferences, pulled from NotificationManagerService.java
+ */
+message PackageNotificationPreferences {
+    // Uid under which the package is installed.
+    optional int32 uid = 1;
+    // Notification importance, which specifies when and how a notification is displayed.
+    // Specified under core/java/android/app/NotificationManager.java.
+    optional int32 importance = 2;
+    // Lockscreen visibility as set by the user.
+    optional int32 visibility = 3;
+    // Bitfield mask indicating what fields were locked by the user (see LockableAppfields in
+    // PreferencesHelper.java)
+    optional int32 user_locked_fields = 4;
+}
+
+/**
+ * Atom that contains a list of a package's channel preferences, pulled from
+ * NotificationManagerService.java.
+ */
+message PackageNotificationChannelPreferences {
+    // Uid under which the package is installed.
+    optional int32 uid = 1;
+    // Channel's ID. Should always be available.
+    optional string channel_id = 2;
+    // Channel's name. Should always be available.
+    optional string channel_name = 3;
+    // Channel's description. Optionally set by the channel creator.
+    optional string description = 4;
+    // Notification importance, which specifies when and how a notification is displayed. Specified
+    // under core/java/android/app/NotificationManager.java.
+    optional int32 importance = 5;
+    // Bitmask representing which fields have been set by the user. See field bitmask descriptions
+    // at core/java/android/app/NotificationChannel.java
+    optional int32 user_locked_fields = 6;
+    // Indicates if the channel was deleted by the app.
+    optional bool is_deleted = 7;
+}
+
+/**
+ * Atom that contains a list of a package's channel group preferences, pulled from
+ * NotificationManagerService.java.
+ */
+message PackageNotificationChannelGroupPreferences {
+    // Uid under which the package is installed.
+    optional int32 uid = 1;
+    // Channel Group's ID. Should always be available.
+    optional string group_id = 2;
+    // Channel Group's name. Should always be available.
+    optional string group_name = 3;
+    // Channel Group's description. Optionally set by group creator.
+    optional string description = 4;
+    // Indicates if notifications from this channel group are blocked.
+    optional bool is_blocked = 5;
+    // Bitmask representing which fields have been set by the user. See field bitmask descriptions
+    // at core/java/android/app/NotificationChannelGroup.java
+    optional int32 user_locked_fields = 6;
+}
+
 message PowerProfileProto {
     optional double cpu_suspend = 1;
 
@@ -6986,6 +7049,20 @@
     optional string description = 6;
     optional Method method = 7;
     optional string mime_types = 8;
+
+    optional int64 get_constraints_count =  9;
+    optional int64 get_metadata_count = 10;
+    optional int64 can_handle_count = 11;
+    optional int64 process_drm_info_count = 12;
+    optional int64 acquire_drm_info_count = 13;
+    optional int64 save_rights_count = 14;
+    optional int64 get_original_mime_type_count = 15;
+    optional int64 get_drm_object_type_count = 16;
+    optional int64 check_rights_status_count = 17;
+    optional int64 remove_rights_count = 18;
+    optional int64 remove_all_rights_count = 19;
+    optional int64 open_convert_session_count = 20;
+    optional int64 open_decrypt_session_count = 21;
 }
 
 /**
@@ -7919,6 +7996,10 @@
     // Total time that was spent performing animations.
     // This is derived from the present-to-present layer histogram
     optional int64 animation_millis = 5;
+    // Total number of event connections tracked by SurfaceFlinger at the time
+    // of this pull. If this number grows prohibitively large, then this can
+    // cause jank due to resource contention.
+    optional int32 event_connection_count = 6;
 }
 
 /**
diff --git a/core/java/android/annotation/NonNull.java b/core/java/android/annotation/NonNull.java
index 927f997..a95bf3b 100644
--- a/core/java/android/annotation/NonNull.java
+++ b/core/java/android/annotation/NonNull.java
@@ -15,14 +15,16 @@
  */
 package android.annotation;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.annotation.SystemApi.Client;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that a parameter, field or method return value can never be null.
  * <p>
@@ -34,5 +36,6 @@
  */
 @Retention(SOURCE)
 @Target({METHOD, PARAMETER, FIELD})
+@SystemApi(client = Client.MODULE_LIBRARIES)
 public @interface NonNull {
 }
diff --git a/core/java/android/annotation/Nullable.java b/core/java/android/annotation/Nullable.java
index b60170b..2fcddfa 100644
--- a/core/java/android/annotation/Nullable.java
+++ b/core/java/android/annotation/Nullable.java
@@ -15,14 +15,16 @@
  */
 package android.annotation;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
 import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.PARAMETER;
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.annotation.SystemApi.Client;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
 /**
  * Denotes that a parameter, field or method return value can be null.
  * <p>
@@ -41,5 +43,6 @@
  */
 @Retention(SOURCE)
 @Target({METHOD, PARAMETER, FIELD})
+@SystemApi(client = Client.MODULE_LIBRARIES)
 public @interface Nullable {
 }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 642f51b..2319dd2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5810,9 +5810,9 @@
                 intent.prepareToLeaveProcess(this);
                 result = ActivityTaskManager.getService()
                     .startActivity(mMainThread.getApplicationThread(), getBasePackageName(),
-                            intent, intent.resolveTypeIfNeeded(getContentResolver()), mToken,
-                            mEmbeddedID, requestCode, ActivityManager.START_FLAG_ONLY_IF_NEEDED,
-                            null, options);
+                            getFeatureId(), intent,
+                            intent.resolveTypeIfNeeded(getContentResolver()), mToken, mEmbeddedID,
+                            requestCode, ActivityManager.START_FLAG_ONLY_IF_NEEDED, null, options);
             } catch (RemoteException e) {
                 // Empty
             }
@@ -6606,8 +6606,8 @@
         try {
             data.prepareToLeaveProcess(this);
             IIntentSender target =
-                ActivityManager.getService().getIntentSender(
-                        ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,
+                ActivityManager.getService().getIntentSenderWithFeature(
+                        ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName, getFeatureId(),
                         mParent == null ? mToken : mParent.mToken,
                         mEmbeddedID, requestCode, new Intent[] { data }, null, flags, null,
                         getUserId());
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 7ee4405..2838ad8 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4316,8 +4316,8 @@
      */
     public static void broadcastStickyIntent(Intent intent, int appOp, int userId) {
         try {
-            getService().broadcastIntent(
-                    null, intent, null, null, Activity.RESULT_OK, null, null,
+            getService().broadcastIntentWithFeature(
+                    null, null, intent, null, null, Activity.RESULT_OK, null, null,
                     null /*permission*/, appOp, null, false, true, userId);
         } catch (RemoteException ex) {
         }
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index c60f7bd..ec11043 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -269,13 +269,16 @@
 
     public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
             long duration, String tag);
-    public abstract int broadcastIntentInPackage(String packageName, int uid, int realCallingUid,
-            int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo,
-            int resultCode, String resultData, Bundle resultExtras, String requiredPermission,
-            Bundle bOptions, boolean serialized, boolean sticky, @UserIdInt int userId,
-            boolean allowBackgroundActivityStarts);
+
+    public abstract int broadcastIntentInPackage(String packageName, @Nullable String featureId,
+            int uid, int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
+            IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
+            String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
+            @UserIdInt int userId, boolean allowBackgroundActivityStarts);
+
     public abstract ComponentName startServiceInPackage(int uid, Intent service,
-            String resolvedType, boolean fgRequired, String callingPackage, @UserIdInt int userId,
+            String resolvedType, boolean fgRequired, String callingPackage,
+            @Nullable String callingFeatureId, @UserIdInt int userId,
             boolean allowBackgroundActivityStarts) throws TransactionTooLargeException;
 
     public abstract void disconnectActivityFromServices(Object connectionHolder);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index fc37af9..367c2f2 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -924,10 +924,12 @@
      * @hide
      */
     public static final int OP_ACTIVATE_PLATFORM_VPN = 94;
+    /** @hide */
+    public static final int OP_LOADER_USAGE_STATS = 95;
 
     /** @hide */
     @UnsupportedAppUsage
-    public static final int _NUM_OP = 95;
+    public static final int _NUM_OP = 96;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1224,6 +1226,9 @@
     public static final String OPSTR_INTERACT_ACROSS_PROFILES = "android:interact_across_profiles";
     /** @hide Start Platform VPN without user intervention */
     public static final String OPSTR_ACTIVATE_PLATFORM_VPN = "android:activate_platform_vpn";
+    /** @hide */
+    @SystemApi
+    public static final String OPSTR_LOADER_USAGE_STATS = "android:loader_usage_stats";
 
 
     /** {@link #sAppOpsToNote} not initialized yet for this op */
@@ -1302,7 +1307,8 @@
             OP_MANAGE_IPSEC_TUNNELS,
             OP_INSTANT_APP_START_FOREGROUND,
             OP_MANAGE_EXTERNAL_STORAGE,
-            OP_INTERACT_ACROSS_PROFILES
+            OP_INTERACT_ACROSS_PROFILES,
+            OP_LOADER_USAGE_STATS,
     };
 
     /**
@@ -1409,6 +1415,7 @@
             OP_MANAGE_EXTERNAL_STORAGE,         // MANAGE_EXTERNAL_STORAGE
             OP_INTERACT_ACROSS_PROFILES,        //INTERACT_ACROSS_PROFILES
             OP_ACTIVATE_PLATFORM_VPN,           // ACTIVATE_PLATFORM_VPN
+            OP_LOADER_USAGE_STATS,              // LOADER_USAGE_STATS
     };
 
     /**
@@ -1510,6 +1517,7 @@
             OPSTR_MANAGE_EXTERNAL_STORAGE,
             OPSTR_INTERACT_ACROSS_PROFILES,
             OPSTR_ACTIVATE_PLATFORM_VPN,
+            OPSTR_LOADER_USAGE_STATS,
     };
 
     /**
@@ -1612,6 +1620,7 @@
             "MANAGE_EXTERNAL_STORAGE",
             "INTERACT_ACROSS_PROFILES",
             "ACTIVATE_PLATFORM_VPN",
+            "LOADER_USAGE_STATS",
     };
 
     /**
@@ -1715,6 +1724,7 @@
             Manifest.permission.MANAGE_EXTERNAL_STORAGE,
             android.Manifest.permission.INTERACT_ACROSS_PROFILES,
             null, // no permission for OP_ACTIVATE_PLATFORM_VPN
+            android.Manifest.permission.LOADER_USAGE_STATS,
     };
 
     /**
@@ -1818,6 +1828,7 @@
             null, // MANAGE_EXTERNAL_STORAGE
             null, // INTERACT_ACROSS_PROFILES
             null, // ACTIVATE_PLATFORM_VPN
+            null, // LOADER_USAGE_STATS
     };
 
     /**
@@ -1920,6 +1931,7 @@
             false, // MANAGE_EXTERNAL_STORAGE
             false, // INTERACT_ACROSS_PROFILES
             false, // ACTIVATE_PLATFORM_VPN
+            false, // LOADER_USAGE_STATS
     };
 
     /**
@@ -2021,6 +2033,7 @@
             AppOpsManager.MODE_DEFAULT, // MANAGE_EXTERNAL_STORAGE
             AppOpsManager.MODE_DEFAULT, // INTERACT_ACROSS_PROFILES
             AppOpsManager.MODE_IGNORED, // ACTIVATE_PLATFORM_VPN
+            AppOpsManager.MODE_DEFAULT, // LOADER_USAGE_STATS
     };
 
     /**
@@ -2126,6 +2139,7 @@
             false, // MANAGE_EXTERNAL_STORAGE
             false, // INTERACT_ACROSS_PROFILES
             false, // ACTIVATE_PLATFORM_VPN
+            false, // LOADER_USAGE_STATS
     };
 
     /**
diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java
index 3febf71..130e2ec 100644
--- a/core/java/android/app/AsyncNotedAppOp.java
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -256,10 +256,10 @@
     };
 
     @DataClass.Generated(
-            time = 1578516519372L,
+            time = 1580158740502L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java",
-            inputSignatures = "private final @android.annotation.IntRange(from=0L, to=94L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.IntRange(from=0L, to=95L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 57cd894..6b5bfda 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1020,7 +1020,7 @@
     public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
         try {
             ActivityTaskManager.getService().startActivityAsUser(
-                mMainThread.getApplicationThread(), getBasePackageName(), intent,
+                mMainThread.getApplicationThread(), getBasePackageName(), getFeatureId(), intent,
                 intent.resolveTypeIfNeeded(getContentResolver()),
                 null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options,
                 user.getIdentifier());
@@ -1102,8 +1102,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                     getUserId());
         } catch (RemoteException e) {
@@ -1119,8 +1119,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                     null, false, false, getUserId());
         } catch (RemoteException e) {
@@ -1134,8 +1134,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                     null, false, false, getUserId());
         } catch (RemoteException e) {
@@ -1149,8 +1149,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                     null, false, false, user.getIdentifier());
         } catch (RemoteException e) {
@@ -1166,8 +1166,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                     options, false, false, getUserId());
         } catch (RemoteException e) {
@@ -1183,8 +1183,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
                     getUserId());
         } catch (RemoteException e) {
@@ -1200,8 +1200,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                     null, true, false, getUserId());
         } catch (RemoteException e) {
@@ -1263,8 +1263,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, rd,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermissions, appOp,
                     options, true, false, getUserId());
         } catch (RemoteException e) {
@@ -1277,9 +1277,10 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(mMainThread.getApplicationThread(),
-                    intent, resolvedType, null, Activity.RESULT_OK, null, null, null,
-                    AppOpsManager.OP_NONE, null, false, false, user.getIdentifier());
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
+                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
+                    user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1299,8 +1300,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                     options, false, false, user.getIdentifier());
         } catch (RemoteException e) {
@@ -1316,8 +1317,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                    mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
                     user.getIdentifier());
         } catch (RemoteException e) {
@@ -1367,8 +1368,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, rd,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermissions,
                     appOp, options, true, false, user.getIdentifier());
         } catch (RemoteException e) {
@@ -1408,8 +1409,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
                 getUserId());
         } catch (RemoteException e) {
@@ -1444,8 +1445,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, rd,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
                     AppOpsManager.OP_NONE, null, true, true, getUserId());
         } catch (RemoteException e) {
@@ -1476,8 +1477,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
                     user.getIdentifier());
         } catch (RemoteException e) {
@@ -1491,8 +1492,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, false, true,
                 user.getIdentifier());
         } catch (RemoteException e) {
@@ -1526,8 +1527,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntent(
-                mMainThread.getApplicationThread(), intent, resolvedType, rd,
+            ActivityManager.getService().broadcastIntentWithFeature(
+                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
                     AppOpsManager.OP_NONE, null, true, true, user.getIdentifier());
         } catch (RemoteException e) {
@@ -1612,9 +1613,9 @@
             }
         }
         try {
-            final Intent intent = ActivityManager.getService().registerReceiver(
-                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
-                    broadcastPermission, userId, flags);
+            final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
+                    mMainThread.getApplicationThread(), mBasePackageName, getFeatureId(), rd,
+                    filter, broadcastPermission, userId, flags);
             if (intent != null) {
                 intent.setExtrasClassLoader(getClassLoader());
                 intent.prepareToEnterProcess();
@@ -1687,9 +1688,9 @@
             validateServiceIntent(service);
             service.prepareToLeaveProcess(this);
             ComponentName cn = ActivityManager.getService().startService(
-                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
-                            getContentResolver()), requireForeground,
-                            getOpPackageName(), user.getIdentifier());
+                    mMainThread.getApplicationThread(), service,
+                    service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
+                    getOpPackageName(), getFeatureId(), user.getIdentifier());
             if (cn != null) {
                 if (cn.getPackageName().equals("!")) {
                     throw new SecurityException(
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index cb6a476..83fa9d7 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -104,25 +104,38 @@
     // Special low-level communication with activity manager.
     void handleApplicationCrash(in IBinder app,
             in ApplicationErrorReport.ParcelableCrashInfo crashInfo);
-    @UnsupportedAppUsage
+    /** @deprecated Use {@link #startActivityWithFeature} instead */
+    @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link android.content.Context#startActivity(android.content.Intent)} instead")
     int startActivity(in IApplicationThread caller, in String callingPackage, in Intent intent,
             in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
             int flags, in ProfilerInfo profilerInfo, in Bundle options);
+    int startActivityWithFeature(in IApplicationThread caller, in String callingPackage,
+            in String callingFeatureId, in Intent intent, in String resolvedType,
+            in IBinder resultTo, in String resultWho, int requestCode, int flags,
+            in ProfilerInfo profilerInfo, in Bundle options);
     @UnsupportedAppUsage
     void unhandledBack();
     @UnsupportedAppUsage
     boolean finishActivity(in IBinder token, int code, in Intent data, int finishTask);
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter)} instead")
     Intent registerReceiver(in IApplicationThread caller, in String callerPackage,
             in IIntentReceiver receiver, in IntentFilter filter,
             in String requiredPermission, int userId, int flags);
+    Intent registerReceiverWithFeature(in IApplicationThread caller, in String callerPackage,
+            in String callingFeatureId, in IIntentReceiver receiver, in IntentFilter filter,
+            in String requiredPermission, int userId, int flags);
     @UnsupportedAppUsage
     void unregisterReceiver(in IIntentReceiver receiver);
-    @UnsupportedAppUsage
+    /** @deprecated Use {@link #broadcastIntentWithFeature} instead */
+    @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link android.content.Context#sendBroadcast(android.content.Intent)} instead")
     int broadcastIntent(in IApplicationThread caller, in Intent intent,
             in String resolvedType, in IIntentReceiver resultTo, int resultCode,
             in String resultData, in Bundle map, in String[] requiredPermissions,
             int appOp, in Bundle options, boolean serialized, boolean sticky, int userId);
+    int broadcastIntentWithFeature(in IApplicationThread caller, in String callingFeatureId,
+            in Intent intent, in String resolvedType, in IIntentReceiver resultTo, int resultCode,
+            in String resultData, in Bundle map, in String[] requiredPermissions,
+            int appOp, in Bundle options, boolean serialized, boolean sticky, int userId);
     void unbroadcastIntent(in IApplicationThread caller, in Intent intent, int userId);
     @UnsupportedAppUsage
     oneway void finishReceiver(in IBinder who, int resultCode, in String resultData, in Bundle map,
@@ -145,7 +158,8 @@
     boolean refContentProvider(in IBinder connection, int stableDelta, int unstableDelta);
     PendingIntent getRunningServiceControlPanel(in ComponentName service);
     ComponentName startService(in IApplicationThread caller, in Intent service,
-            in String resolvedType, boolean requireForeground, in String callingPackage, int userId);
+            in String resolvedType, boolean requireForeground, in String callingPackage,
+            in String callingFeatureId, int userId);
     @UnsupportedAppUsage
     int stopService(in IApplicationThread caller, in Intent service,
             in String resolvedType, int userId);
@@ -226,10 +240,14 @@
     ParceledListSlice getRecentTasks(int maxNum, int flags, int userId);
     @UnsupportedAppUsage
     oneway void serviceDoneExecuting(in IBinder token, int type, int startId, int res);
-    @UnsupportedAppUsage
+    /** @deprecated  Use {@link #getIntentSenderWithFeature} instead */
+    @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link PendingIntent#getIntentSender()} instead")
     IIntentSender getIntentSender(int type, in String packageName, in IBinder token,
             in String resultWho, int requestCode, in Intent[] intents, in String[] resolvedTypes,
             int flags, in Bundle options, int userId);
+    IIntentSender getIntentSenderWithFeature(int type, in String packageName, in String featureId,
+            in IBinder token, in String resultWho, int requestCode, in Intent[] intents,
+            in String[] resolvedTypes, int flags, in Bundle options, int userId);
     void cancelIntentSender(in IIntentSender sender);
     String getPackageForIntentSender(in IIntentSender sender);
     void registerIntentSenderCancelListener(in IIntentSender sender, in IResultReceiver receiver);
@@ -355,11 +373,16 @@
     boolean isIntentSenderAnActivity(in IIntentSender sender);
     boolean isIntentSenderAForegroundService(in IIntentSender sender);
     boolean isIntentSenderABroadcast(in IIntentSender sender);
-    @UnsupportedAppUsage
+    /** @deprecated Use {@link startActivityAsUserWithFeature} instead */
+    @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@code android.content.Context#createContextAsUser(android.os.UserHandle, int)} and {@link android.content.Context#startActivity(android.content.Intent)} instead")
     int startActivityAsUser(in IApplicationThread caller, in String callingPackage,
             in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
             int requestCode, int flags, in ProfilerInfo profilerInfo,
             in Bundle options, int userId);
+    int startActivityAsUserWithFeature(in IApplicationThread caller, in String callingPackage,
+            in String callingFeatureId, in Intent intent, in String resolvedType,
+            in IBinder resultTo, in String resultWho, int requestCode, int flags,
+            in ProfilerInfo profilerInfo, in Bundle options, int userId);
     @UnsupportedAppUsage
     int stopUser(int userid, boolean force, in IStopUserCallback callback);
     /**
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index be2f144..180507c 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -85,16 +85,17 @@
  * {@hide}
  */
 interface IActivityTaskManager {
-    int startActivity(in IApplicationThread caller, in String callingPackage, in Intent intent,
-            in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
+    int startActivity(in IApplicationThread caller, in String callingPackage,
+            in String callingFeatureId, in Intent intent, in String resolvedType,
+            in IBinder resultTo, in String resultWho, int requestCode,
             int flags, in ProfilerInfo profilerInfo, in Bundle options);
     int startActivities(in IApplicationThread caller, in String callingPackage,
-            in Intent[] intents, in String[] resolvedTypes, in IBinder resultTo,
-            in Bundle options, int userId);
+            in String callingFeatureId, in Intent[] intents, in String[] resolvedTypes,
+            in IBinder resultTo, in Bundle options, int userId);
     int startActivityAsUser(in IApplicationThread caller, in String callingPackage,
-            in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
-            int requestCode, int flags, in ProfilerInfo profilerInfo,
-            in Bundle options, int userId);
+            in String callingFeatureId, in Intent intent, in String resolvedType,
+            in IBinder resultTo, in String resultWho, int requestCode, int flags,
+            in ProfilerInfo profilerInfo, in Bundle options, int userId);
     boolean startNextMatchingActivity(in IBinder callingActivity,
             in Intent intent, in Bundle options);
     int startActivityIntentSender(in IApplicationThread caller,
@@ -102,19 +103,19 @@
             in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
             int flagsMask, int flagsValues, in Bundle options);
     WaitResult startActivityAndWait(in IApplicationThread caller, in String callingPackage,
-            in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
-            int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
-            int userId);
+            in String callingFeatureId, in Intent intent, in String resolvedType,
+            in IBinder resultTo, in String resultWho, int requestCode, int flags,
+            in ProfilerInfo profilerInfo, in Bundle options, int userId);
     int startActivityWithConfig(in IApplicationThread caller, in String callingPackage,
-            in Intent intent, in String resolvedType, in IBinder resultTo, in String resultWho,
-            int requestCode, int startFlags, in Configuration newConfig,
-            in Bundle options, int userId);
-    int startVoiceActivity(in String callingPackage, int callingPid, int callingUid,
-            in Intent intent, in String resolvedType, in IVoiceInteractionSession session,
-            in IVoiceInteractor interactor, int flags, in ProfilerInfo profilerInfo,
-            in Bundle options, int userId);
-    int startAssistantActivity(in String callingPackage, int callingPid, int callingUid,
-            in Intent intent, in String resolvedType, in Bundle options, int userId);
+            in String callingFeatureId, in Intent intent, in String resolvedType,
+            in IBinder resultTo, in String resultWho, int requestCode, int startFlags,
+            in Configuration newConfig, in Bundle options, int userId);
+    int startVoiceActivity(in String callingPackage, in String callingFeatureId, int callingPid,
+            int callingUid, in Intent intent, in String resolvedType,
+            in IVoiceInteractionSession session, in IVoiceInteractor interactor, int flags,
+            in ProfilerInfo profilerInfo, in Bundle options, int userId);
+    int startAssistantActivity(in String callingPackage, in String callingFeatureId, int callingPid,
+            int callingUid, in Intent intent, in String resolvedType, in Bundle options, int userId);
     void startRecentsActivity(in Intent intent, in IAssistDataReceiver assistDataReceiver,
             in IRecentsAnimationRunner recentsAnimationRunner);
     int startActivityFromRecents(int taskId, in Bundle options);
diff --git a/core/java/android/app/IAppTask.aidl b/core/java/android/app/IAppTask.aidl
index 3ce7190..f41d705 100644
--- a/core/java/android/app/IAppTask.aidl
+++ b/core/java/android/app/IAppTask.aidl
@@ -27,7 +27,7 @@
     @UnsupportedAppUsage
     ActivityManager.RecentTaskInfo getTaskInfo();
     void moveToFront(in IApplicationThread appThread, in String callingPackage);
-    int startActivity(IBinder whoThread, String callingPackage,
+    int startActivity(IBinder whoThread, String callingPackage, String callingFeatureId,
             in Intent intent, String resolvedType, in Bundle options);
     void setExcludeFromRecents(boolean exclude);
 }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 62c905d..18932c6 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1721,7 +1721,7 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
             int result = ActivityTaskManager.getService()
-                .startActivity(whoThread, who.getBasePackageName(), intent,
+                .startActivity(whoThread, who.getBasePackageName(), who.getFeatureId(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
                         token, target != null ? target.mEmbeddedID : null,
                         requestCode, 0, null, options);
@@ -1794,8 +1794,8 @@
                 resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver());
             }
             int result = ActivityTaskManager.getService()
-                .startActivities(whoThread, who.getBasePackageName(), intents, resolvedTypes,
-                        token, options, userId);
+                .startActivities(whoThread, who.getBasePackageName(), who.getFeatureId(), intents,
+                        resolvedTypes, token, options, userId);
             checkStartActivityResult(result, intents[0]);
             return result;
         } catch (RemoteException e) {
@@ -1861,7 +1861,7 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
             int result = ActivityTaskManager.getService()
-                .startActivity(whoThread, who.getBasePackageName(), intent,
+                .startActivity(whoThread, who.getBasePackageName(), who.getFeatureId(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
                         token, target, requestCode, 0, null, options);
             checkStartActivityResult(result, intent);
@@ -1928,8 +1928,8 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
             int result = ActivityTaskManager.getService()
-                .startActivityAsUser(whoThread, who.getBasePackageName(), intent,
-                        intent.resolveTypeIfNeeded(who.getContentResolver()),
+                .startActivityAsUser(whoThread, who.getBasePackageName(), who.getFeatureId(),
+                        intent, intent.resolveTypeIfNeeded(who.getContentResolver()),
                         token, resultWho,
                         requestCode, 0, null, options, user.getIdentifier());
             checkStartActivityResult(result, intent);
@@ -2022,7 +2022,8 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
             int result = appTask.startActivity(whoThread.asBinder(), who.getBasePackageName(),
-                    intent, intent.resolveTypeIfNeeded(who.getContentResolver()), options);
+                    who.getFeatureId(), intent,
+                    intent.resolveTypeIfNeeded(who.getContentResolver()), options);
             checkStartActivityResult(result, intent);
         } catch (RemoteException e) {
             throw new RuntimeException("Failure from system", e);
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index b8348c7..f68c929 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -354,8 +354,8 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(context);
             IIntentSender target =
-                ActivityManager.getService().getIntentSender(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                ActivityManager.getService().getIntentSenderWithFeature(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
                     null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, options, context.getUserId());
@@ -380,8 +380,8 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(context);
             IIntentSender target =
-                ActivityManager.getService().getIntentSender(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                ActivityManager.getService().getIntentSenderWithFeature(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
                     null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, options, user.getIdentifier());
@@ -497,8 +497,8 @@
         }
         try {
             IIntentSender target =
-                ActivityManager.getService().getIntentSender(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                ActivityManager.getService().getIntentSenderWithFeature(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
                     null, null, requestCode, intents, resolvedTypes, flags, options,
                     context.getUserId());
             return target != null ? new PendingIntent(target) : null;
@@ -523,8 +523,8 @@
         }
         try {
             IIntentSender target =
-                ActivityManager.getService().getIntentSender(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
+                ActivityManager.getService().getIntentSenderWithFeature(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
                     null, null, requestCode, intents, resolvedTypes,
                     flags, options, user.getIdentifier());
             return target != null ? new PendingIntent(target) : null;
@@ -575,8 +575,8 @@
         try {
             intent.prepareToLeaveProcess(context);
             IIntentSender target =
-                ActivityManager.getService().getIntentSender(
-                    ActivityManager.INTENT_SENDER_BROADCAST, packageName,
+                ActivityManager.getService().getIntentSenderWithFeature(
+                    ActivityManager.INTENT_SENDER_BROADCAST, packageName, context.getFeatureId(),
                     null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, null, userHandle.getIdentifier());
@@ -654,8 +654,8 @@
         try {
             intent.prepareToLeaveProcess(context);
             IIntentSender target =
-                ActivityManager.getService().getIntentSender(
-                    serviceKind, packageName,
+                ActivityManager.getService().getIntentSenderWithFeature(
+                    serviceKind, packageName, context.getFeatureId(),
                     null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, null, context.getUserId());
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a52ee54..48eab92 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -5860,21 +5860,27 @@
      * a secure video output. See {@link android.view.Display#FLAG_SECURE} for more details about
      * secure surfaces and secure displays.
      * <p>
-     * The calling device admin must be a device or profile owner. If it is not, a security
+     * This method can be called on the {@link DevicePolicyManager} instance, returned by
+     * {@link #getParentProfileInstance(ComponentName)}, where the calling device admin must be
+     * the profile owner of an organization-owned managed profile. If it is not, a security
      * exception will be thrown.
      * <p>
+     * If the caller is device owner or called on the parent instance by a profile owner of an
+     * organization-owned managed profile, then the restriction will be applied to all users.
+     * <p>
      * From version {@link android.os.Build.VERSION_CODES#M} disabling screen capture also blocks
      * assist requests for all activities of the relevant user.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param disabled Whether screen capture is disabled or not.
-     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     * @throws SecurityException if {@code admin} is not a device or profile owner or if
+     *                           called on the parent profile and the {@code admin} is not a
+     *                           profile owner of an organization-owned managed profile.
      */
     public void setScreenCaptureDisabled(@NonNull ComponentName admin, boolean disabled) {
-        throwIfParentInstance("setScreenCaptureDisabled");
         if (mService != null) {
             try {
-                mService.setScreenCaptureDisabled(admin, disabled);
+                mService.setScreenCaptureDisabled(admin, disabled, mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -5884,11 +5890,16 @@
     /**
      * Determine whether or not screen capture has been disabled by the calling
      * admin, if specified, or all admins.
-     * @param admin The name of the admin component to check, or {@code null} to check whether any admins
-     * have disabled screen capture.
+     * <p>
+     * This method can be called on the {@link DevicePolicyManager} instance,
+     * returned by {@link #getParentProfileInstance(ComponentName)}, where the caller must be
+     * the profile owner of an organization-owned managed profile (the calling admin must be
+     * specified).
+     *
+     * @param admin The name of the admin component to check, or {@code null} to check whether any
+     *              admins have disabled screen capture.
      */
     public boolean getScreenCaptureDisabled(@Nullable ComponentName admin) {
-        throwIfParentInstance("getScreenCaptureDisabled");
         return getScreenCaptureDisabled(admin, myUserId());
     }
 
@@ -5896,7 +5907,7 @@
     public boolean getScreenCaptureDisabled(@Nullable ComponentName admin, int userHandle) {
         if (mService != null) {
             try {
-                return mService.getScreenCaptureDisabled(admin, userHandle);
+                return mService.getScreenCaptureDisabled(admin, userHandle, mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index e6c89d9..80fa871 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -187,4 +187,22 @@
      * @hide
      */
     public abstract List<String> getAllCrossProfilePackages();
+
+    /**
+     * Sends the {@code intent} to the packages with cross profile capabilities.
+     *
+     * <p>This means the application must have the {@code crossProfile} property and the
+     * corresponding permissions, defined by
+     * {@link
+     * android.content.pm.CrossProfileAppsInternal#verifyPackageHasInteractAcrossProfilePermission}.
+     *
+     * <p>Note: This method doesn't modify {@code intent} but copies it before use.
+     *
+     * @param intent Template for the intent sent to the package.
+     * @param parentHandle Handle of the user that will receive the intents.
+     * @param requiresPermission If false, all packages with the {@code crossProfile} property
+     *                           will receive the intent.
+     */
+    public abstract void broadcastIntentToCrossProfileManifestReceiversAsUser(Intent intent,
+            UserHandle parentHandle, boolean requiresPermission);
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index d2672eb..8ef25bd 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -121,8 +121,8 @@
     void setCameraDisabled(in ComponentName who, boolean disabled, boolean parent);
     boolean getCameraDisabled(in ComponentName who, int userHandle, boolean parent);
 
-    void setScreenCaptureDisabled(in ComponentName who, boolean disabled);
-    boolean getScreenCaptureDisabled(in ComponentName who, int userHandle);
+    void setScreenCaptureDisabled(in ComponentName who, boolean disabled, boolean parent);
+    boolean getScreenCaptureDisabled(in ComponentName who, int userHandle, boolean parent);
 
     void setKeyguardDisabledFeatures(in ComponentName who, int which, boolean parent);
     int getKeyguardDisabledFeatures(in ComponentName who, int userHandle, boolean parent);
diff --git a/core/java/android/app/prediction/AppPredictionContext.java b/core/java/android/app/prediction/AppPredictionContext.java
index 8fc7e8d..d14238b 100644
--- a/core/java/android/app/prediction/AppPredictionContext.java
+++ b/core/java/android/app/prediction/AppPredictionContext.java
@@ -100,16 +100,6 @@
                 && mPackageName.equals(other.mPackageName);
     }
 
-    @NonNull
-    @Override
-    public String toString() {
-        return new StringBuilder(this.getClass().getSimpleName())
-                .append("[mUiSurface=").append(mUiSurface)
-                .append(",mPackageName=").append(mPackageName)
-                .append(",mPredictedTargetCount=").append(mPredictedTargetCount)
-                .append(",mExtras=").append(mExtras.toString()).append("]").toString();
-    }
-
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index b998539..d251ba9 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1389,7 +1389,7 @@
                 pw.println(prefix + "fullBackupContent="
                         + (fullBackupContent < 0 ? "false" : "true"));
             }
-            pw.println("crossProfile=" + (crossProfile ? "true" : "false"));
+            pw.println(prefix + "crossProfile=" + (crossProfile ? "true" : "false"));
             if (networkSecurityConfigRes != 0) {
                 pw.println(prefix + "networkSecurityConfigRes=0x"
                         + Integer.toHexString(networkSecurityConfigRes));
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 25f0ff3..50841c3 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -87,6 +87,7 @@
             mService.startActivityAsUser(
                     mContext.getIApplicationThread(),
                     mContext.getPackageName(),
+                    mContext.getFeatureId(),
                     component,
                     targetUser.getIdentifier(),
                     true);
@@ -114,6 +115,7 @@
             mService.startActivityAsUserByIntent(
                     mContext.getIApplicationThread(),
                     mContext.getPackageName(),
+                    mContext.getFeatureId(),
                     intent,
                     targetUser.getIdentifier());
         } catch (RemoteException ex) {
@@ -139,7 +141,8 @@
     public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
         try {
             mService.startActivityAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(), component, targetUser.getIdentifier(), false);
+                    mContext.getPackageName(), mContext.getFeatureId(), component,
+                    targetUser.getIdentifier(), false);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/pm/CrossProfileAppsInternal.java b/core/java/android/content/pm/CrossProfileAppsInternal.java
new file mode 100644
index 0000000..9ff4417
--- /dev/null
+++ b/core/java/android/content/pm/CrossProfileAppsInternal.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.UserIdInt;
+
+/**
+ * Exposes internal methods from {@link com.android.server.pm.CrossProfileAppsServiceImpl} to other
+ * system server classes.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class CrossProfileAppsInternal {
+    /**
+     * Returns whether the package has the necessary permissions to communicate cross-profile.
+     *
+     * <p>This means having at least one of these conditions:
+     * <ul>
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS_FULL} granted.
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS} granted.
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_PROFILES} granted, or the corresponding
+     *     AppOps {@code android:interact_across_profiles} is set to "allow".
+     * </ul>
+     */
+    public abstract boolean verifyPackageHasInteractAcrossProfilePermission(String packageName,
+            @UserIdInt int userId) throws PackageManager.NameNotFoundException;
+
+    /**
+     * Returns whether the package has the necessary permissions to communicate cross-profile.
+     *
+     * <p>This means having at least one of these conditions:
+     * <ul>
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS_FULL} granted.
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_USERS} granted.
+     *     <li>{@code Manifest.permission.INTERACT_ACROSS_PROFILES} granted, or the corresponding
+     *     AppOps {@code android:interact_across_profiles} is set to "allow".
+     * </ul>
+     */
+    public abstract boolean verifyUidHasInteractAcrossProfilePermission(String packageName,
+            int uid);
+}
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index a69b988..98bf2dd 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -28,13 +28,14 @@
  */
 interface ICrossProfileApps {
     void startActivityAsUser(in IApplicationThread caller, in String callingPackage,
-            in ComponentName component, int userId, boolean launchMainActivity);
+            in String callingFeatureId, in ComponentName component, int userId,
+            boolean launchMainActivity);
     void startActivityAsUserByIntent(in IApplicationThread caller, in String callingPackage,
-                        in Intent intent, int userId);
+            in String callingFeatureId, in Intent intent, int userId);
     List<UserHandle> getTargetUserProfiles(in String callingPackage);
     boolean canInteractAcrossProfiles(in String callingPackage);
     boolean canRequestInteractAcrossProfiles(in String callingPackage);
     void setInteractAcrossProfilesAppOp(in String packageName, int newMode);
     boolean canConfigureInteractAcrossProfiles(in String packageName);
     void resetInteractAcrossProfilesAppOps(in List<String> packageNames);
-}
\ No newline at end of file
+}
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 38a9ac4a..b5f4f80 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -49,13 +49,13 @@
     ActivityInfo resolveActivity(
             String callingPackage, in ComponentName component, in UserHandle user);
     void startSessionDetailsActivityAsUser(in IApplicationThread caller, String callingPackage,
-                in PackageInstaller.SessionInfo sessionInfo, in Rect sourceBounds, in Bundle opts,
-                in UserHandle user);
+                String callingFeatureId, in PackageInstaller.SessionInfo sessionInfo,
+                in Rect sourceBounds, in Bundle opts, in UserHandle user);
     void startActivityAsUser(in IApplicationThread caller, String callingPackage,
-            in ComponentName component, in Rect sourceBounds,
+            String callingFeatureId, in ComponentName component, in Rect sourceBounds,
             in Bundle opts, in UserHandle user);
-    void showAppDetailsAsUser(in IApplicationThread caller,
-            String callingPackage, in ComponentName component, in Rect sourceBounds,
+    void showAppDetailsAsUser(in IApplicationThread caller, String callingPackage,
+            String callingFeatureId, in ComponentName component, in Rect sourceBounds,
             in Bundle opts, in UserHandle user);
     boolean isPackageEnabled(String callingPackage, String packageName, in UserHandle user);
     Bundle getSuspendedPackageLauncherExtras(String packageName, in UserHandle user);
@@ -72,7 +72,7 @@
             int flags, in UserHandle user);
     void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
             in UserHandle user);
-    boolean startShortcut(String callingPackage, String packageName, String id,
+    boolean startShortcut(String callingPackage, String packageName, String featureId, String id,
             in Rect sourceBounds, in Bundle startActivityOptions, int userId);
 
     int getShortcutIconResId(String callingPackage, String packageName, String id,
diff --git a/core/java/android/content/pm/InstantAppRequest.java b/core/java/android/content/pm/InstantAppRequest.java
index f692db1..84f5021 100644
--- a/core/java/android/content/pm/InstantAppRequest.java
+++ b/core/java/android/content/pm/InstantAppRequest.java
@@ -35,6 +35,8 @@
     public final String resolvedType;
     /** The name of the package requesting the instant application */
     public final String callingPackage;
+    /** The feature in the package requesting the instant application */
+    public final String callingFeatureId;
     /** Whether or not the requesting package was an instant app */
     public final boolean isRequesterInstantApp;
     /** ID of the user requesting the instant application */
@@ -57,13 +59,15 @@
     public final String token;
 
     public InstantAppRequest(AuxiliaryResolveInfo responseObj, Intent origIntent,
-            String resolvedType, String callingPackage, boolean isRequesterInstantApp,
-            int userId, Bundle verificationBundle, boolean resolveForStart,
-            @Nullable int[] hostDigestPrefixSecure, @NonNull String token) {
+            String resolvedType, String callingPackage, @Nullable String callingFeatureId,
+            boolean isRequesterInstantApp, int userId, Bundle verificationBundle,
+            boolean resolveForStart, @Nullable int[] hostDigestPrefixSecure,
+            @NonNull String token) {
         this.responseObj = responseObj;
         this.origIntent = origIntent;
         this.resolvedType = resolvedType;
         this.callingPackage = callingPackage;
+        this.callingFeatureId = callingFeatureId;
         this.isRequesterInstantApp = isRequesterInstantApp;
         this.userId = userId;
         this.verificationBundle = verificationBundle;
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 70603b4..5aa0208 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -720,7 +720,7 @@
         }
         try {
             mService.startActivityAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(),
+                    mContext.getPackageName(), mContext.getFeatureId(),
                     component, sourceBounds, opts, user);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
@@ -738,8 +738,8 @@
             @Nullable Rect sourceBounds, @Nullable Bundle opts) {
         try {
             mService.startSessionDetailsActivityAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(), sessionInfo, sourceBounds, opts,
-                    sessionInfo.getUser());
+                    mContext.getPackageName(), mContext.getFeatureId(), sessionInfo, sourceBounds,
+                    opts, sessionInfo.getUser());
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -759,7 +759,7 @@
         logErrorForInvalidProfileAccess(user);
         try {
             mService.showAppDetailsAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(),
+                    mContext.getPackageName(), mContext.getFeatureId(),
                     component, sourceBounds, opts, user);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
@@ -1351,9 +1351,9 @@
             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
             int userId) {
         try {
-            final boolean success =
-                    mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
-                    sourceBounds, startActivityOptions, userId);
+            final boolean success = mService.startShortcut(mContext.getPackageName(), packageName,
+                    null /* default featureId */, shortcutId, sourceBounds, startActivityOptions,
+                    userId);
             if (!success) {
                 throw new ActivityNotFoundException("Shortcut could not be started");
             }
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 9bef2e1..8f3cb93 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -95,7 +95,7 @@
     }
 
     /**
-     * Retrieves the native CameraMetadata* as a Java long.
+     * Retrieves the native std::shared_ptr<CameraMetadata*>* as a Java long.
      * Returns 0 if mNativeInstance is null.
      *
      * @hide
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index aefe66f..df77f52 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -1680,7 +1680,7 @@
     }
 
     @UnsupportedAppUsage
-    private long mMetadataPtr; // native CameraMetadata*
+    private long mMetadataPtr; // native std::shared_ptr<CameraMetadata>*
 
     private native long nativeAllocate();
     private native long nativeAllocateCopy(CameraMetadataNative other)
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 3c39d15..d0091440 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -252,8 +252,8 @@
                 @NonNull PersistableBundle additionalInfo) {
             mNetwork = network;
             mReportTimestamp = reportTimestamp;
-            mLinkProperties = linkProperties;
-            mNetworkCapabilities = networkCapabilities;
+            mLinkProperties = new LinkProperties(linkProperties);
+            mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
             mAdditionalInfo = additionalInfo;
         }
 
@@ -433,6 +433,12 @@
         /** The detection method used to identify the suspected data stall */
         @DetectionMethod private final int mDetectionMethod;
 
+        /** LinkProperties available on the Network at the reported timestamp */
+        @NonNull private final LinkProperties mLinkProperties;
+
+        /** NetworkCapabilities available on the Network at the reported timestamp */
+        @NonNull private final NetworkCapabilities mNetworkCapabilities;
+
         /** PersistableBundle that may contain additional information on the suspected data stall */
         @NonNull private final PersistableBundle mStallDetails;
 
@@ -446,16 +452,23 @@
          * @param network The Network for which this DataStallReport applies
          * @param reportTimestamp The timestamp for the report
          * @param detectionMethod The detection method used to identify this data stall
+         * @param linkProperties The LinkProperties available on network at reportTimestamp
+         * @param networkCapabilities The NetworkCapabilities available on network at
+         *     reportTimestamp
          * @param stallDetails A PersistableBundle that may contain additional info about the report
          */
         public DataStallReport(
                 @NonNull Network network,
                 long reportTimestamp,
                 @DetectionMethod int detectionMethod,
+                @NonNull LinkProperties linkProperties,
+                @NonNull NetworkCapabilities networkCapabilities,
                 @NonNull PersistableBundle stallDetails) {
             mNetwork = network;
             mReportTimestamp = reportTimestamp;
             mDetectionMethod = detectionMethod;
+            mLinkProperties = new LinkProperties(linkProperties);
+            mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
             mStallDetails = stallDetails;
         }
 
@@ -488,6 +501,26 @@
         }
 
         /**
+         * Returns the LinkProperties available when this report was taken.
+         *
+         * @return LinkProperties available on the Network at the reported timestamp
+         */
+        @NonNull
+        public LinkProperties getLinkProperties() {
+            return new LinkProperties(mLinkProperties);
+        }
+
+        /**
+         * Returns the NetworkCapabilities when this report was taken.
+         *
+         * @return NetworkCapabilities available on the Network at the reported timestamp
+         */
+        @NonNull
+        public NetworkCapabilities getNetworkCapabilities() {
+            return new NetworkCapabilities(mNetworkCapabilities);
+        }
+
+        /**
          * Returns a PersistableBundle with additional info for this report.
          *
          * <p>Gets a bundle with details about the suspected data stall including information
@@ -513,12 +546,20 @@
             return mReportTimestamp == that.mReportTimestamp
                     && mDetectionMethod == that.mDetectionMethod
                     && mNetwork.equals(that.mNetwork)
+                    && mLinkProperties.equals(that.mLinkProperties)
+                    && mNetworkCapabilities.equals(that.mNetworkCapabilities)
                     && persistableBundleEquals(mStallDetails, that.mStallDetails);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mNetwork, mReportTimestamp, mDetectionMethod, mStallDetails);
+            return Objects.hash(
+                    mNetwork,
+                    mReportTimestamp,
+                    mDetectionMethod,
+                    mLinkProperties,
+                    mNetworkCapabilities,
+                    mStallDetails);
         }
 
         /** {@inheritDoc} */
@@ -533,6 +574,8 @@
             dest.writeParcelable(mNetwork, flags);
             dest.writeLong(mReportTimestamp);
             dest.writeInt(mDetectionMethod);
+            dest.writeParcelable(mLinkProperties, flags);
+            dest.writeParcelable(mNetworkCapabilities, flags);
             dest.writeParcelable(mStallDetails, flags);
         }
 
@@ -544,6 +587,8 @@
                                 in.readParcelable(null),
                                 in.readLong(),
                                 in.readInt(),
+                                in.readParcelable(null),
+                                in.readParcelable(null),
                                 in.readParcelable(null));
                     }
 
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 96d7a80..2f536ff 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -18,6 +18,7 @@
 
 import static android.os.Process.CLAT_UID;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
@@ -35,6 +36,8 @@
 
 import java.io.CharArrayWriter;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Map;
@@ -100,6 +103,19 @@
      */
     public static final int SET_DBG_VPN_OUT = 1002;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "SET_" }, value = {
+            SET_ALL,
+            SET_DEFAULT,
+            SET_FOREGROUND,
+            SET_DEBUG_START,
+            SET_DBG_VPN_IN,
+            SET_DBG_VPN_OUT
+    })
+    public @interface State {
+    }
+
     /**
      * Include all interfaces when filtering
      * @hide
@@ -120,6 +136,17 @@
     /** {@link #metered} value where metered data is accounted. */
     public static final int METERED_YES = 1;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "METERED_" }, value = {
+            METERED_ALL,
+            METERED_NO,
+            METERED_YES
+    })
+    public @interface Meteredness {
+    }
+
+
     /**
      * {@link #roaming} value to account for all roaming states.
      * @hide
@@ -130,6 +157,16 @@
     /** {@link #roaming} value where roaming data is accounted. */
     public static final int ROAMING_YES = 1;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "ROAMING_" }, value = {
+            ROAMING_ALL,
+            ROAMING_NO,
+            ROAMING_YES
+    })
+    public @interface Roaming {
+    }
+
     /**
      * {@link #onDefaultNetwork} value to account for all default network states.
      * @hide
@@ -140,6 +177,16 @@
     /** {@link #onDefaultNetwork} value to account for usage while the default network. */
     public static final int DEFAULT_NETWORK_YES = 1;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "DEFAULT_NETWORK_" }, value = {
+            DEFAULT_NETWORK_ALL,
+            DEFAULT_NETWORK_NO,
+            DEFAULT_NETWORK_YES
+    })
+    public @interface DefaultNetwork {
+    }
+
     /**
      * Denotes a request for stats at the interface level.
      * @hide
@@ -151,6 +198,15 @@
      */
     public static final int STATS_PER_UID = 1;
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "STATS_PER_" }, value = {
+            STATS_PER_IFACE,
+            STATS_PER_UID
+    })
+    public @interface StatsType {
+    }
+
     private static final String CLATD_INTERFACE_PREFIX = "v4-";
     // Delta between IPv4 header (20b) and IPv6 header (40b).
     // Used for correct stats accounting on clatd interfaces.
@@ -263,9 +319,9 @@
                     rxBytes, rxPackets, txBytes, txPackets, operations);
         }
 
-        public Entry(@Nullable String iface, int uid, int set, int tag, int metered, int roaming,
-                 int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
-                 long operations) {
+        public Entry(@Nullable String iface, int uid, @State int set, int tag,
+                @Meteredness int metered, @Roaming int roaming, @DefaultNetwork int defaultNetwork,
+                long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
             this.iface = iface;
             this.uid = uid;
             this.set = set;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 8d09461..dbe3b7b 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -214,6 +214,20 @@
      */
     public static final int FSVERITY_CERT_UID = 1075;
 
+    /**
+     * GID that gives write access to app-private data directories on external
+     * storage (used on devices without sdcardfs only).
+     * @hide
+     */
+    public static final int EXT_DATA_RW_GID = 1078;
+
+    /**
+     * GID that gives write access to app-private OBB directories on external
+     * storage (used on devices without sdcardfs only).
+     * @hide
+     */
+    public static final int EXT_OBB_RW_GID = 1079;
+
     /** {@hide} */
     public static final int NOBODY_UID = 9999;
 
diff --git a/core/java/android/os/StatsServiceManager.java b/core/java/android/os/StatsServiceManager.java
index d032e98..de07e92 100644
--- a/core/java/android/os/StatsServiceManager.java
+++ b/core/java/android/os/StatsServiceManager.java
@@ -18,17 +18,16 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.annotation.SystemApi.Client;
 
 /**
  * Provides a way to register and obtain the system service binder objects managed by the stats
  * service.
  *
  * <p> Only the statsd mainline module will be able to access an instance of this class.
- *
- * TODO(b/148225705) Change to @SystemApi(client=MODULE_LIBRARIES) when the build system is ready.
  * @hide
  */
-@SystemApi
+@SystemApi(client = Client.MODULE_LIBRARIES)
 public class StatsServiceManager {
     /**
      * @hide
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 3508b70..50cc764 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -177,11 +177,7 @@
     }
 
     /**
-     * Set whether tracing is enabled in this process.  Tracing is disabled shortly after Zygote
-     * initializes and re-enabled after processes fork from Zygote.  This is done because Zygote
-     * has no way to be notified about changes to the tracing tags, and if Zygote ever reads and
-     * caches the tracing tags, forked processes will inherit those stale tags.
-     *
+     * Set whether tracing is enabled in this process.
      * @hide
      */
     public static void setTracingEnabled(boolean enabled, int debugFlags) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 44c2fbb..7c4ec8e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1771,6 +1771,15 @@
             = "android.settings.NOTIFICATION_SETTINGS";
 
     /**
+     * Activity Action: Show notification history screen.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_NOTIFICATION_HISTORY
+            = "android.settings.NOTIFICATION_HISTORY";
+
+    /**
      * Activity Action: Show app listing settings, filtered by those that send notifications.
      *
      * @hide
@@ -2997,6 +3006,7 @@
         private static final HashSet<String> MOVED_TO_SECURE;
         static {
             MOVED_TO_SECURE = new HashSet<>(30);
+            MOVED_TO_SECURE.add(Secure.ADAPTIVE_SLEEP);
             MOVED_TO_SECURE.add(Secure.ANDROID_ID);
             MOVED_TO_SECURE.add(Secure.HTTP_PROXY);
             MOVED_TO_SECURE.add(Secure.LOCATION_PROVIDERS_ALLOWED);
@@ -3952,6 +3962,7 @@
 
         /**
          * Control whether to enable adaptive sleep mode.
+         * @deprecated Use {@link android.provider.Settings.Secure#ADAPTIVE_SLEEP} instead.
          * @hide
          */
         public static final String ADAPTIVE_SLEEP = "adaptive_sleep";
@@ -4744,7 +4755,6 @@
             PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_FOR_VR);
             PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_FOR_VR_FLOAT);
             PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_MODE);
-            PUBLIC_SETTINGS.add(ADAPTIVE_SLEEP);
             PUBLIC_SETTINGS.add(MODE_RINGER_STREAMS_AFFECTED);
             PUBLIC_SETTINGS.add(MUTE_STREAMS_AFFECTED);
             PUBLIC_SETTINGS.add(VIBRATE_ON);
@@ -5798,6 +5808,12 @@
         }
 
         /**
+         * Control whether to enable adaptive sleep mode.
+         * @hide
+         */
+        public static final String ADAPTIVE_SLEEP = "adaptive_sleep";
+
+        /**
          * @deprecated Use {@link android.provider.Settings.Global#DEVELOPMENT_SETTINGS_ENABLED}
          * instead
          */
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 0edd013..cecfe24 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -339,6 +339,7 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     public void onDataShareRequest(@NonNull DataShareRequest request,
             @NonNull DataShareCallback callback) {
         if (sVerbose) Log.v(TAG, "onDataShareRequest()");
diff --git a/core/java/android/service/contentcapture/DataShareCallback.java b/core/java/android/service/contentcapture/DataShareCallback.java
index e3c7bb3..5df8a4b 100644
--- a/core/java/android/service/contentcapture/DataShareCallback.java
+++ b/core/java/android/service/contentcapture/DataShareCallback.java
@@ -19,6 +19,7 @@
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 
 import java.util.concurrent.Executor;
 
@@ -32,6 +33,7 @@
  * @hide
  **/
 @SystemApi
+@TestApi
 public interface DataShareCallback {
 
     /** Accept the data share.
diff --git a/core/java/android/service/contentcapture/DataShareReadAdapter.java b/core/java/android/service/contentcapture/DataShareReadAdapter.java
index b9fce68..a481ec8 100644
--- a/core/java/android/service/contentcapture/DataShareReadAdapter.java
+++ b/core/java/android/service/contentcapture/DataShareReadAdapter.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.ParcelFileDescriptor;
 
 /**
@@ -27,6 +28,7 @@
  * @hide
  **/
 @SystemApi
+@TestApi
 public interface DataShareReadAdapter {
 
     /**
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 36f2c62..8e6f77b 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1314,7 +1314,7 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(mContext);
             int res = mSystemService.startVoiceActivity(mToken, intent,
-                    intent.resolveType(mContext.getContentResolver()));
+                    intent.resolveType(mContext.getContentResolver()), mContext.getFeatureId());
             Instrumentation.checkStartActivityResult(res, intent);
         } catch (RemoteException e) {
         }
@@ -1342,7 +1342,7 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(mContext);
             int res = mSystemService.startAssistantActivity(mToken, intent,
-                    intent.resolveType(mContext.getContentResolver()));
+                    intent.resolveType(mContext.getContentResolver()), mContext.getFeatureId());
             Instrumentation.checkStartActivityResult(res, intent);
         } catch (RemoteException e) {
         }
diff --git a/core/java/android/telephony/WapPushManagerConnector.java b/core/java/android/telephony/WapPushManagerConnector.java
deleted file mode 100644
index a9df506..0000000
--- a/core/java/android/telephony/WapPushManagerConnector.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2019 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.telephony;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.IBinder;
-import android.os.RemoteException;
-
-import com.android.internal.telephony.IWapPushManager;
-import com.android.internal.util.Preconditions;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * APIs for platform to connect to the WAP push manager service.
- *
- * <p>To start connection, {@link #bindToWapPushManagerService} should be called.
- *
- * <p>Upon completion {@link #unbindWapPushManagerService} should be called to unbind the service.
- *
- * @hide
- */
-@SystemApi
-public final class WapPushManagerConnector {
-    private final Context mContext;
-
-    private volatile WapPushManagerConnection mConnection;
-    private volatile IWapPushManager mWapPushManager;
-    private String mWapPushManagerPackage;
-
-    /**
-     * The {@link android.content.Intent} that must be declared as handled by the
-     * WAP push manager service.
-     * @hide
-     */
-    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
-    public static final String SERVICE_INTERFACE =
-            "com.android.internal.telephony.IWapPushManager";
-
-    /** @hide */
-    @IntDef(flag = true, prefix = {"RESULT_"}, value = {
-            RESULT_MESSAGE_HANDLED,
-            RESULT_APP_QUERY_FAILED,
-            RESULT_SIGNATURE_NO_MATCH,
-            RESULT_INVALID_RECEIVER_NAME,
-            RESULT_EXCEPTION_CAUGHT,
-            RESULT_FURTHER_PROCESSING,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface ProcessMessageResult{}
-
-    /** {@link #processMessage} return value: Message is handled. */
-    public static final int RESULT_MESSAGE_HANDLED = 0x1;
-    /** {@link #processMessage} return value: Application ID or content type was not found. */
-    public static final int RESULT_APP_QUERY_FAILED = 0x2;
-    /** {@link #processMessage} return value: Receiver application signature check failed. */
-    public static final int RESULT_SIGNATURE_NO_MATCH = 0x4;
-    /** {@link #processMessage} return value: Receiver application was not found. */
-    public static final int RESULT_INVALID_RECEIVER_NAME = 0x8;
-    /** {@link #processMessage} return value: Unknown exception. */
-    public static final int RESULT_EXCEPTION_CAUGHT = 0x10;
-    /** {@link #processMessage} return value: further processing needed. */
-    public static final int RESULT_FURTHER_PROCESSING = 0x8000;
-
-    /** The application package name of the WAP push manager service. */
-    private static final String SERVICE_PACKAGE = "com.android.smspush";
-
-    public WapPushManagerConnector(@NonNull Context context) {
-        mContext = context;
-    }
-
-    /**
-     * Binds to the WAP push manager service. This method should be called exactly once.
-     *
-     * @return {@code true} upon successfully binding to a service, {@code false} otherwise
-     */
-    public boolean bindToWapPushManagerService() {
-        Preconditions.checkState(mConnection == null);
-
-        Intent intent = new Intent(SERVICE_INTERFACE);
-        ComponentName component = intent.resolveSystemService(mContext.getPackageManager(), 0);
-        intent.setComponent(component);
-        mConnection = new WapPushManagerConnection();
-        if (component != null
-                && mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
-            mWapPushManagerPackage = component.getPackageName();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns the package name of WAP push manager service application connected to,
-     * or {@code null} if not connected.
-     */
-    @Nullable
-    public String getConnectedWapPushManagerServicePackage() {
-        return mWapPushManagerPackage;
-    }
-
-    /**
-     * Processes WAP push message and triggers the {@code intent}.
-     *
-     * @see RESULT_MESSAGE_HANDLED
-     * @see RESULT_APP_QUERY_FAILED
-     * @see RESULT_SIGNATURE_NO_MATCH
-     * @see RESULT_INVALID_RECEIVER_NAME
-     * @see RESULT_EXCEPTION_CAUGHT
-     * @see RESULT_FURTHER_PROCESSING
-     */
-    @ProcessMessageResult
-    public int processMessage(
-            @NonNull String applicationId, @NonNull String contentType, @NonNull Intent intent) {
-        try {
-            return mWapPushManager.processMessage(applicationId, contentType, intent);
-        } catch (NullPointerException | RemoteException e) {
-            return RESULT_EXCEPTION_CAUGHT;
-        }
-    }
-
-    /**
-     * Unbinds the WAP push manager service. This method should be called exactly once.
-     */
-    public void unbindWapPushManagerService() {
-        Preconditions.checkNotNull(mConnection);
-
-        mContext.unbindService(mConnection);
-        mConnection = null;
-    }
-
-    private class WapPushManagerConnection implements ServiceConnection {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            // Because we have bound to an explicit
-            // service that is running in our own process, we can
-            // cast its IBinder to a concrete class and directly access it.
-            mWapPushManager = IWapPushManager.Stub.asInterface(service);
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            mWapPushManager = null;
-        }
-
-        @Override
-        public void onNullBinding(ComponentName name) {
-            onServiceDisconnected(name);
-        }
-
-        @Override
-        public void onBindingDied(ComponentName name) {
-            onServiceDisconnected(name);
-        }
-    }
-}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 5fdac81..afeb6c3 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -50,6 +50,8 @@
     /** @hide */
     public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED =
             "settings_do_not_restore_preserved";
+    /** @hide */
+    public static final String SETTINGS_SCHEDULES_FLAG = "settings_schedules";
 
     private static final Map<String, String> DEFAULT_FLAGS;
 
@@ -76,6 +78,7 @@
         DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "false");
 
         DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
+        DEFAULT_FLAGS.put(SETTINGS_SCHEDULES_FLAG, "false");
     }
 
     /**
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 54de1bb..2548068 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -32,6 +32,7 @@
 import android.annotation.Nullable;
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.util.ArraySet;
@@ -39,7 +40,6 @@
 import android.util.Pair;
 import android.util.Property;
 import android.util.SparseArray;
-import android.view.InputDevice.MotionRange;
 import android.view.InsetsSourceConsumer.ShowResult;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.SurfaceControl.Transaction;
@@ -242,13 +242,15 @@
 
         PendingControlRequest(@InsetsType int types, WindowInsetsAnimationControlListener listener,
                 long durationMs, Interpolator interpolator, @AnimationType int animationType,
-                @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
+                @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
+                CancellationSignal cancellationSignal) {
             this.types = types;
             this.listener = listener;
             this.durationMs = durationMs;
             this.interpolator = interpolator;
             this.animationType = animationType;
             this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
+            this.cancellationSignal = cancellationSignal;
         }
 
         final @InsetsType int types;
@@ -257,6 +259,7 @@
         final Interpolator interpolator;
         final @AnimationType int animationType;
         final @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation;
+        final CancellationSignal cancellationSignal;
     }
 
     private final String TAG = "InsetsControllerImpl";
@@ -455,10 +458,13 @@
             PendingControlRequest pendingRequest = mPendingImeControlRequest;
             mPendingImeControlRequest = null;
             mHandler.removeCallbacks(mPendingControlTimeout);
-            controlAnimationUnchecked(pendingRequest.types, pendingRequest.listener, mFrame,
+            CancellationSignal cancellationSignal = controlAnimationUnchecked(
+                    pendingRequest.types,
+                    pendingRequest.listener, mFrame,
                     true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator,
                     false /* fade */, pendingRequest.animationType,
                     pendingRequest.layoutInsetsDuringAnimation);
+            pendingRequest.cancellationSignal.setOnCancelListener(cancellationSignal::cancel);
             return;
         }
 
@@ -504,35 +510,39 @@
     }
 
     @Override
-    public void controlWindowInsetsAnimation(@InsetsType int types, long durationMs,
+    public CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, long durationMs,
             @Nullable Interpolator interpolator,
             @NonNull WindowInsetsAnimationControlListener listener) {
-        controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs, interpolator,
-                ANIMATION_TYPE_USER);
+        return controlWindowInsetsAnimation(types, listener, false /* fromIme */, durationMs,
+                interpolator, ANIMATION_TYPE_USER);
     }
 
-    private void controlWindowInsetsAnimation(@InsetsType int types,
+    private CancellationSignal controlWindowInsetsAnimation(@InsetsType int types,
             WindowInsetsAnimationControlListener listener, boolean fromIme, long durationMs,
             @Nullable Interpolator interpolator, @AnimationType int animationType) {
         // If the frame of our window doesn't span the entire display, the control API makes very
         // little sense, as we don't deal with negative insets. So just cancel immediately.
         if (!mState.getDisplayFrame().equals(mFrame)) {
             listener.onCancelled();
-            return;
+            CancellationSignal cancellationSignal = new CancellationSignal();
+            cancellationSignal.cancel();
+            return cancellationSignal;
         }
-        controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator,
+        return controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator,
                 false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types));
     }
 
-    private void controlAnimationUnchecked(@InsetsType int types,
+    private CancellationSignal controlAnimationUnchecked(@InsetsType int types,
             WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
             long durationMs, Interpolator interpolator, boolean fade,
             @AnimationType int animationType,
             @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
+        CancellationSignal cancellationSignal = new CancellationSignal();
         if (types == 0) {
             // nothing to animate.
             listener.onCancelled();
-            return;
+            cancellationSignal.cancel();
+            return cancellationSignal;
         }
         cancelExistingControllers(types);
 
@@ -546,21 +556,31 @@
         if (!imeReady) {
             // IME isn't ready, all requested types will be animated once IME is ready
             abortPendingImeControlRequest();
-            mPendingImeControlRequest = new PendingControlRequest(types, listener, durationMs,
-                    interpolator, animationType, layoutInsetsDuringAnimation);
+            final PendingControlRequest request = new PendingControlRequest(types,
+                    listener, durationMs,
+                    interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal);
+            mPendingImeControlRequest = request;
             mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS);
-            return;
+            cancellationSignal.setOnCancelListener(() -> {
+                if (mPendingImeControlRequest == request) {
+                    abortPendingImeControlRequest();
+                }
+            });
+            return cancellationSignal;
         }
 
         if (typesReady == 0) {
             listener.onCancelled();
-            return;
+            cancellationSignal.cancel();
+            return cancellationSignal;
         }
 
         final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls,
                 frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
                 layoutInsetsDuringAnimation);
         mRunningAnimations.add(new RunningAnimation(controller, animationType));
+        cancellationSignal.setOnCancelListener(controller::onCancelled);
+        return cancellationSignal;
     }
 
     /**
@@ -680,7 +700,14 @@
             }
             mApplier = new SyncRtSurfaceTransactionApplier(mViewRoot.mView);
         }
-        mApplier.scheduleApply(params);
+        if (mViewRoot.mView.isHardwareAccelerated()) {
+            mApplier.scheduleApply(params);
+        } else {
+            // Window doesn't support hardware acceleration, no synchronization for now.
+            // TODO(b/149342281): use mViewRoot.mSurface.getNextFrameNumber() to sync on every
+            //  frame instead.
+            mApplier.applyParams(new Transaction(), -1 /* frame */, params);
+        }
     }
 
     void notifyControlRevoked(InsetsSourceConsumer consumer) {
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index abe44f4..15252b1 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -65,20 +65,33 @@
                 return;
             }
             Transaction t = new Transaction();
-            for (int i = params.length - 1; i >= 0; i--) {
-                SurfaceParams surfaceParams = params[i];
-                SurfaceControl surface = surfaceParams.surface;
-                t.deferTransactionUntil(surface, mTargetSc, frame);
-                applyParams(t, surfaceParams, mTmpFloat9);
-            }
-            t.setEarlyWakeup();
-            t.apply();
+            applyParams(t, frame, params);
         });
 
         // Make sure a frame gets scheduled.
         mTargetViewRootImpl.getView().invalidate();
     }
 
+    /**
+     * Applies surface parameters on the next frame.
+     * @param t transaction to apply all parameters in.
+     * @param frame frame to synchronize to. Set -1 when sync is not required.
+     * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
+     *               this method to avoid synchronization issues.
+     */
+    void applyParams(Transaction t, long frame, final SurfaceParams... params) {
+        for (int i = params.length - 1; i >= 0; i--) {
+            SurfaceParams surfaceParams = params[i];
+            SurfaceControl surface = surfaceParams.surface;
+            if (frame > 0) {
+                t.deferTransactionUntil(surface, mTargetSc, frame);
+            }
+            applyParams(t, surfaceParams, mTmpFloat9);
+        }
+        t.setEarlyWakeup();
+        t.apply();
+    }
+
     public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
         if ((params.flags & FLAG_MATRIX) != 0) {
             t.setMatrix(params.surface, params.matrix, tmpFloat9);
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index ea8e7388..27e92e5 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Insets;
+import android.os.CancellationSignal;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsAnimationCallback.InsetsAnimation;
 import android.view.animation.Interpolator;
@@ -154,13 +155,15 @@
      *                     {@link InsetsAnimation#getInterpolatedFraction()}.
      * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
      *                 windows are ready to be controlled, among other callbacks.
-     *
+     * @return A cancellation signal that the caller can use to cancel the request to obtain
+     *         control, or once they have control, to cancel the control.
      * @see InsetsAnimation#getFraction()
      * @see InsetsAnimation#getInterpolatedFraction()
      * @see InsetsAnimation#getInterpolator()
      * @see InsetsAnimation#getDurationMillis()
      */
-    void controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
+    @NonNull
+    CancellationSignal controlWindowInsetsAnimation(@InsetsType int types, long durationMillis,
             @Nullable Interpolator interpolator,
             @NonNull WindowInsetsAnimationControlListener listener);
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index dad7671..e731323 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3687,6 +3687,8 @@
                     return "always";
                 case LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER:
                     return "never";
+                case LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES:
+                    return "shortEdges";
                 default:
                     return "unknown(" + mode + ")";
             }
diff --git a/core/java/android/webkit/WebViewProviderResponse.java b/core/java/android/webkit/WebViewProviderResponse.java
index b58cc4bb..02e48dd 100644
--- a/core/java/android/webkit/WebViewProviderResponse.java
+++ b/core/java/android/webkit/WebViewProviderResponse.java
@@ -16,6 +16,8 @@
 
 package android.webkit;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.pm.PackageInfo;
 import android.os.Parcel;
@@ -30,7 +32,7 @@
     }
 
     // aidl stuff
-    public static final @android.annotation.NonNull Parcelable.Creator<WebViewProviderResponse> CREATOR =
+    public static final @NonNull Parcelable.Creator<WebViewProviderResponse> CREATOR =
         new Parcelable.Creator<WebViewProviderResponse>() {
             public WebViewProviderResponse createFromParcel(Parcel in) {
                 return new WebViewProviderResponse(in);
@@ -58,6 +60,7 @@
     }
 
     @UnsupportedAppUsage
+    @Nullable
     public final PackageInfo packageInfo;
     public final int status;
 }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4e4a983..46b8b77 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -5111,8 +5111,10 @@
                     int lineRight = (int) layout.getLineRight(line);
                     lineRight += mTextView.getTotalPaddingLeft() - mTextView.getScrollX();
                     mMagnifierAnimator.mMagnifier.setSourceHorizontalBounds(lineLeft, lineRight);
+                    mMagnifierAnimator.mMagnifier.show(showPosInView.x, showPosInView.y);
+                } else {
+                  mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
                 }
-                mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
                 updateHandlesVisibility();
             } else {
                 dismissMagnifier();
@@ -5248,9 +5250,6 @@
         private boolean mIsInActionMode;
         // The timestamp for the last up event, which is used for double tap detection.
         private long mLastUpTime;
-        // The text height of the font of the text view, which is used to calculate the Y coordinate
-        // of the touch through events.
-        private float mTextHeight;
 
         // The delta height applied to the insertion handle view.
         private final int mDeltaHeight;
@@ -5403,8 +5402,6 @@
                     if (ev.getEventTime() - mLastUpTime < ViewConfiguration.getDoubleTapTimeout()) {
                         stopTextActionMode();  // Avoid crash when double tap and drag backwards.
                     }
-                    final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics();
-                    mTextHeight = fontMetrics.descent - fontMetrics.ascent;
                     mTouchState.setIsOnHandle(true);
                     break;
                 case MotionEvent.ACTION_UP:
@@ -5443,6 +5440,10 @@
         }
 
         private MotionEvent transformEventForTouchThrough(MotionEvent ev) {
+            final Layout layout = mTextView.getLayout();
+            final int line = layout.getLineForOffset(getCurrentCursorOffset());
+            final int textHeight =
+                    layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line);
             // Transforms the touch events to screen coordinates.
             // And also shift up to make the hit point is on the text.
             // Note:
@@ -5450,7 +5451,7 @@
             //  - The revised Y should be at the top of the text.
             Matrix m = new Matrix();
             m.setTranslate(ev.getRawX() - ev.getX() + (getMeasuredWidth() >> 1) - mTouchDownX,
-                    ev.getRawY() - ev.getY() - mTouchDownY - mTextHeight);
+                    ev.getRawY() - ev.getY() - (textHeight >> 1) - mTouchDownY);
             ev.transform(m);
             // Transforms the touch events to text view coordinates.
             mTextView.toLocalMotionEvent(ev);
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 50da6ce..a6a5ec5 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -45,6 +45,7 @@
 import android.os.HandlerThread;
 import android.os.Message;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.PixelCopy;
@@ -76,6 +77,8 @@
     // the Handler of this Thread when the copy is finished.
     private static final HandlerThread sPixelCopyHandlerThread =
             new HandlerThread("magnifier pixel copy result handler");
+    // The width of the ramp region in DP on the left & right sides of the fish-eye effect.
+    private static final float FISHEYE_RAMP_WIDTH = 12f;
 
     // The view to which this magnifier is attached.
     private final View mView;
@@ -151,6 +154,8 @@
     // The horizontal bounds of the content source in pixels, relative to the view.
     private int mLeftBound = Integer.MIN_VALUE;
     private int mRightBound = Integer.MAX_VALUE;
+    // The width of the ramp region in pixels on the left & right sides of the fish-eye effect.
+    private final int mRamp;
 
     /**
      * Initializes a magnifier.
@@ -232,6 +237,8 @@
         mBottomContentBound = params.mBottomContentBound;
         // The view's surface coordinates will not be updated until the magnifier is first shown.
         mViewCoordinatesInSurface = new int[2];
+        mRamp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, FISHEYE_RAMP_WIDTH,
+                mView.getContext().getResources().getDisplayMetrics());
     }
 
     static {
@@ -303,16 +310,54 @@
             // The magnifier center is the same as source center in new style.
             magnifierCenterX = mClampedCenterZoomCoords.x - mViewCoordinatesInSurface[0];
             magnifierCenterY = mClampedCenterZoomCoords.y - mViewCoordinatesInSurface[1];
+
+            // mLeftBound & mRightBound (typically the text line left/right) is for magnified
+            // content. However the PixelCopy requires the pre-magnified bounds.
+            // The below logic calculates the leftBound & rightBound for the pre-magnified bounds.
+            final float rampPre =
+                    (mSourceWidth - (mSourceWidth - 2 * mRamp) / mZoom) / 2;
+
+            // Calculates the pre-zoomed left edge.
+            // The leftEdge moves from the left of view towards to sourceCenterX, considering the
+            // fisheye-like zooming.
+            final float x0 = sourceCenterX - mSourceWidth / 2;
+            final float rampX0 = x0 + mRamp;
+            float leftEdge = 0;
+            if (leftEdge > rampX0) {
+                // leftEdge is in the zoom range, the distance from leftEdge to sourceCenterX
+                // should reduce per mZoom.
+                leftEdge = sourceCenterX - (sourceCenterX - leftEdge) / mZoom;
+            } else if (leftEdge > x0) {
+                // leftEdge is in the ramp range, the distance from leftEdge to rampX0 should
+                // increase per ramp zoom (ramp / rampPre).
+                leftEdge = x0 + rampPre - (rampX0 - leftEdge) * rampPre / mRamp;
+            }
+            int leftBound = Math.min(Math.max((int) leftEdge, mLeftBound), mRightBound);
+
+            // Calculates the pre-zoomed right edge.
+            // The rightEdge moves from the right of view towards to sourceCenterX, considering the
+            // fisheye-like zooming.
+            final float x1 = sourceCenterX + mSourceWidth / 2;
+            final float rampX1 = x1 - mRamp;
+            float rightEdge = mView.getWidth();
+            if (rightEdge < rampX1) {
+                // rightEdge is in the zoom range, the distance from rightEdge to sourceCenterX
+                // should reduce per mZoom.
+                rightEdge = sourceCenterX + (rightEdge - sourceCenterX) / mZoom;
+            } else if (rightEdge < x1) {
+                // rightEdge is in the ramp range, the distance from rightEdge to rampX1 should
+                // increase per ramp zoom (ramp / rampPre).
+                rightEdge = x1 - rampPre + (rightEdge - rampX1) * rampPre / mRamp;
+            }
+            int rightBound = Math.max(leftBound, Math.min((int) rightEdge, mRightBound));
+
             // Gets the startX for new style, which should be bounded by the horizontal bounds.
             // Also calculates the left/right cut width for pixel copy.
-            final int left = startX;
-            final int right = startX + mSourceWidth;
-            final int leftBound = mViewCoordinatesInSurface[0] + Math.max(0, mLeftBound);
-            final int rightBound =
-                    mViewCoordinatesInSurface[0] + Math.min(mView.getWidth(), mRightBound);
-            startX = Math.max(left, leftBound);
-            mLeftCutWidth = Math.max(0, leftBound - left);
-            mRightCutWidth = Math.max(0, right - rightBound);
+            leftBound += mViewCoordinatesInSurface[0];
+            rightBound += mViewCoordinatesInSurface[0];
+            mLeftCutWidth = Math.max(0, leftBound - startX);
+            mRightCutWidth = Math.max(0, startX + mSourceWidth - rightBound);
+            startX = Math.max(startX, leftBound);
         }
         obtainWindowCoordinates(magnifierCenterX, magnifierCenterY);
 
@@ -322,7 +367,7 @@
                 synchronized (mLock) {
                     mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(),
                             mParentSurface.mSurfaceControl, mWindowWidth, mWindowHeight, mZoom,
-                            mWindowElevation, mWindowCornerRadius,
+                            mRamp, mWindowElevation, mWindowCornerRadius,
                             mOverlay != null ? mOverlay : new ColorDrawable(Color.TRANSPARENT),
                             Handler.getMain() /* draw the magnifier on the UI thread */, mLock,
                             mCallback, mIsFishEyeStyle);
@@ -854,8 +899,6 @@
         // The z of the magnifier surface, defining its z order in the list of
         // siblings having the same parent surface (usually the main app surface).
         private static final int SURFACE_Z = 5;
-        // The width of the ramp region in pixels on the left & right sides of the fish-eye effect.
-        private static final int FISHEYE_RAMP_WIDTH = 30;
 
         // Display associated to the view the magnifier is attached to.
         private final Display mDisplay;
@@ -905,6 +948,8 @@
         private Bitmap mCurrentContent;
 
         private final float mZoom;
+        // The width of the ramp region in pixels on the left & right sides of the fish-eye effect.
+        private final int mRamp;
         // Whether is in the new magnifier style.
         private boolean mIsFishEyeStyle;
         // The mesh matrix for the fish-eye effect.
@@ -915,7 +960,7 @@
 
         InternalPopupWindow(final Context context, final Display display,
                 final SurfaceControl parentSurfaceControl, final int width, final int height,
-                final float zoom, final float elevation, final float cornerRadius,
+                final float zoom, final int ramp, final float elevation, final float cornerRadius,
                 final Drawable overlay, final Handler handler, final Object lock,
                 final Callback callback, final boolean isFishEyeStyle) {
             mDisplay = display;
@@ -926,6 +971,7 @@
             mContentWidth = width;
             mContentHeight = height;
             mZoom = zoom;
+            mRamp = ramp;
             mOffsetX = (int) (1.05f * elevation);
             mOffsetY = (int) (1.05f * elevation);
             // Setup the surface we will use for drawing the content and shadow.
@@ -995,14 +1041,13 @@
             final float h = mContentHeight;
             final float h0 = h / mZoom;
             final float dh = h - h0;
-            final float ramp = FISHEYE_RAMP_WIDTH;
             mMeshLeft = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)];
             mMeshRight = new float[2 * (mMeshWidth + 1) * (mMeshHeight + 1)];
             for (int i = 0; i < 2 * (mMeshWidth + 1) * (mMeshHeight + 1); i += 2) {
                 // Calculates X value.
                 final int colIndex = i % (2 * (mMeshWidth + 1)) / 2;
-                mMeshLeft[i] = (float) colIndex * ramp / mMeshWidth;
-                mMeshRight[i] = w - ramp + colIndex * ramp / mMeshWidth;
+                mMeshLeft[i] = (float) colIndex * mRamp / mMeshWidth;
+                mMeshRight[i] = w - mRamp + colIndex * mRamp / mMeshWidth;
 
                 // Calculates Y value.
                 final int rowIndex = i / 2 / (mMeshWidth + 1);
@@ -1174,14 +1219,13 @@
                     final Paint paint = new Paint();
                     paint.setFilterBitmap(true);
                     if (mIsFishEyeStyle) {
-                        final int ramp = FISHEYE_RAMP_WIDTH;
                         final int margin =
-                            (int)((mContentWidth - (mContentWidth - 2 * ramp) / mZoom) / 2);
+                            (int)((mContentWidth - (mContentWidth - 2 * mRamp) / mZoom) / 2);
 
                         // Draws the middle part.
                         final Rect srcRect = new Rect(margin, 0, w - margin, h);
                         final Rect dstRect = new Rect(
-                            ramp, 0, mContentWidth - ramp, mContentHeight);
+                            mRamp, 0, mContentWidth - mRamp, mContentHeight);
                         canvas.drawBitmap(mBitmap, srcRect, dstRect, paint);
 
                         // Draws the left/right parts with mesh matrixes.
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index ab8c19e..78e8518 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -15,15 +15,25 @@
  */
 package com.android.internal.app;
 
+import android.annotation.DrawableRes;
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.app.AppGlobals;
+import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.PagerAdapter;
 import com.android.internal.widget.ViewPager;
@@ -213,26 +223,115 @@
 
     abstract @Nullable ViewGroup getInactiveAdapterView();
 
-    boolean rebuildActiveTab(boolean post) {
-        return rebuildTab(getActiveListAdapter(), post);
+    /**
+     * Rebuilds the tab that is currently visible to the user.
+     * <p>Returns {@code true} if rebuild has completed.
+     */
+    boolean rebuildActiveTab(boolean doPostProcessing) {
+        return rebuildTab(getActiveListAdapter(), doPostProcessing);
     }
 
-    boolean rebuildInactiveTab(boolean post) {
+    /**
+     * Rebuilds the tab that is not currently visible to the user, if such one exists.
+     * <p>Returns {@code true} if rebuild has completed.
+     */
+    boolean rebuildInactiveTab(boolean doPostProcessing) {
         if (getItemCount() == 1) {
             return false;
         }
-        return rebuildTab(getInactiveListAdapter(), post);
+        return rebuildTab(getInactiveListAdapter(), doPostProcessing);
+    }
+
+    private int userHandleToPageIndex(UserHandle userHandle) {
+        if (userHandle == getPersonalListAdapter().mResolverListController.getUserHandle()) {
+            return PROFILE_PERSONAL;
+        } else {
+            return PROFILE_WORK;
+        }
     }
 
     private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
         UserHandle listUserHandle = activeListAdapter.getUserHandle();
-        if (UserHandle.myUserId() != listUserHandle.getIdentifier() &&
-                !hasAppsInOtherProfile(activeListAdapter)) {
-            // TODO(arangelov): Show empty state UX here
+        UserManager userManager = mContext.getSystemService(UserManager.class);
+        if (listUserHandle == mWorkProfileUserHandle
+                && userManager.isQuietModeEnabled(mWorkProfileUserHandle)) {
+            showEmptyState(activeListAdapter,
+                    R.drawable.ic_work_apps_off,
+                    R.string.resolver_turn_on_work_apps,
+                    R.string.resolver_turn_on_work_apps_explanation,
+                    (View.OnClickListener) v ->
+                            userManager.requestQuietModeEnabled(false, mWorkProfileUserHandle));
             return false;
-        } else {
-            return activeListAdapter.rebuildList(doPostProcessing);
         }
+        if (UserHandle.myUserId() != listUserHandle.getIdentifier()) {
+            if (!hasCrossProfileIntents(activeListAdapter.getIntents(),
+                    UserHandle.myUserId(), listUserHandle.getIdentifier())) {
+                if (listUserHandle == mPersonalProfileUserHandle) {
+                    showEmptyState(activeListAdapter,
+                            R.drawable.ic_sharing_disabled,
+                            R.string.resolver_cant_share_with_personal_apps,
+                            R.string.resolver_cant_share_cross_profile_explanation);
+                } else {
+                    showEmptyState(activeListAdapter,
+                            R.drawable.ic_sharing_disabled,
+                            R.string.resolver_cant_share_with_work_apps,
+                            R.string.resolver_cant_share_cross_profile_explanation);
+                }
+                return false;
+            }
+        }
+        return activeListAdapter.rebuildList(doPostProcessing);
+    }
+
+    void showEmptyState(ResolverListAdapter listAdapter) {
+        UserHandle listUserHandle = listAdapter.getUserHandle();
+        if (UserHandle.myUserId() == listUserHandle.getIdentifier()
+                || !hasAppsInOtherProfile(listAdapter)) {
+            showEmptyState(listAdapter,
+                    R.drawable.ic_no_apps,
+                    R.string.resolver_no_apps_available,
+                    R.string.resolver_no_apps_available_explanation);
+        }
+    }
+
+    private void showEmptyState(ResolverListAdapter activeListAdapter,
+            @DrawableRes int iconRes, @StringRes int titleRes, @StringRes int subtitleRes) {
+        showEmptyState(activeListAdapter, iconRes, titleRes, subtitleRes, /* buttonOnClick */ null);
+    }
+
+    private void showEmptyState(ResolverListAdapter activeListAdapter,
+            @DrawableRes int iconRes, @StringRes int titleRes, @StringRes int subtitleRes,
+            View.OnClickListener buttonOnClick) {
+        ProfileDescriptor descriptor = getItem(
+                userHandleToPageIndex(activeListAdapter.getUserHandle()));
+        descriptor.rootView.findViewById(R.id.resolver_list).setVisibility(View.GONE);
+        View emptyStateView = descriptor.rootView.findViewById(R.id.resolver_empty_state);
+        emptyStateView.setVisibility(View.VISIBLE);
+
+        ImageView icon = emptyStateView.findViewById(R.id.resolver_empty_state_icon);
+        icon.setImageResource(iconRes);
+
+        TextView title = emptyStateView.findViewById(R.id.resolver_empty_state_title);
+        title.setText(titleRes);
+
+        TextView subtitle = emptyStateView.findViewById(R.id.resolver_empty_state_subtitle);
+        subtitle.setText(subtitleRes);
+
+        Button button = emptyStateView.findViewById(R.id.resolver_empty_state_button);
+        button.setVisibility(buttonOnClick != null ? View.VISIBLE : View.GONE);
+        button.setOnClickListener(buttonOnClick);
+    }
+
+    private boolean hasCrossProfileIntents(List<Intent> intents, int source, int target) {
+        IPackageManager packageManager = AppGlobals.getPackageManager();
+        ContentResolver contentResolver = mContext.getContentResolver();
+        for (Intent intent : intents) {
+            if (IntentForwarderActivity.canForward(intent, source, target, packageManager,
+                    contentResolver) != null) {
+                return true;
+            }
+        }
+        return false;
     }
 
     private boolean hasAppsInOtherProfile(ResolverListAdapter adapter) {
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 5f35622..86d2ed6 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -30,10 +30,12 @@
 import static com.android.internal.util.Preconditions.checkArgument;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityShortcutInfo;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.content.ComponentName;
 import android.content.Context;
@@ -265,6 +267,7 @@
             @ShortcutType int shortcutType) {
         final List<AccessibilityButtonTarget> targets = new ArrayList<>();
         targets.addAll(getAccessibilityServiceTargets(context));
+        targets.addAll(getAccessibilityActivityTargets(context));
         targets.addAll(getWhiteListingServiceTargets(context));
 
         final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
@@ -291,6 +294,24 @@
         return targets;
     }
 
+    private static List<AccessibilityButtonTarget> getAccessibilityActivityTargets(
+            @NonNull Context context) {
+        final AccessibilityManager ams = context.getSystemService(AccessibilityManager.class);
+        final List<AccessibilityShortcutInfo> installedServices =
+                ams.getInstalledAccessibilityShortcutListAsUser(context,
+                        ActivityManager.getCurrentUser());
+        if (installedServices == null) {
+            return Collections.emptyList();
+        }
+
+        final List<AccessibilityButtonTarget> targets = new ArrayList<>(installedServices.size());
+        for (AccessibilityShortcutInfo info : installedServices) {
+            targets.add(new AccessibilityButtonTarget(context, info));
+        }
+
+        return targets;
+    }
+
     private static List<AccessibilityButtonTarget> getWhiteListingServiceTargets(
             @NonNull Context context) {
         final List<AccessibilityButtonTarget> targets = new ArrayList<>();
@@ -540,6 +561,14 @@
             this.mFragmentType = getAccessibilityServiceFragmentType(serviceInfo);
         }
 
+        AccessibilityButtonTarget(@NonNull Context context,
+                @NonNull AccessibilityShortcutInfo shortcutInfo) {
+            this.mId = shortcutInfo.getComponentName().flattenToString();
+            this.mLabel = shortcutInfo.getActivityInfo().loadLabel(context.getPackageManager());
+            this.mDrawable = shortcutInfo.getActivityInfo().loadIcon(context.getPackageManager());
+            this.mFragmentType = AccessibilityServiceFragmentType.BOUNCE;
+        }
+
         AccessibilityButtonTarget(Context context, @NonNull String id, int labelResId,
                 int iconRes, @AccessibilityServiceFragmentType int fragmentType) {
             this.mId = id;
@@ -594,7 +623,7 @@
                 onIntuitiveTargetSelected(target);
                 break;
             case AccessibilityServiceFragmentType.BOUNCE:
-                // Do nothing
+                onBounceTargetSelected(target);
                 break;
             default:
                 throw new IllegalStateException("Unexpected fragment type");
@@ -625,6 +654,15 @@
         switchServiceState(target);
     }
 
+    private void onBounceTargetSelected(AccessibilityButtonTarget target) {
+        final AccessibilityManager ams = getSystemService(AccessibilityManager.class);
+        if (mShortcutType == ACCESSIBILITY_BUTTON) {
+            ams.notifyAccessibilityButtonClicked(getDisplayId(), target.getId());
+        } else if (mShortcutType == ACCESSIBILITY_SHORTCUT_KEY) {
+            ams.performAccessibilityShortcut(target.getId());
+        }
+    }
+
     private void switchServiceState(AccessibilityButtonTarget target) {
         final ComponentName componentName =
                 ComponentName.unflattenFromString(target.getId());
@@ -656,7 +694,7 @@
                 onIntuitiveTargetDeleted(position, componentId);
                 break;
             case AccessibilityServiceFragmentType.BOUNCE:
-                // Do nothing
+                onBounceTargetDeleted(position, componentId);
                 break;
             default:
                 throw new IllegalStateException("Unexpected fragment type");
@@ -694,6 +732,12 @@
         mTargetAdapter.notifyDataSetChanged();
     }
 
+    private void onBounceTargetDeleted(int position, String componentId) {
+        optOutValueFromSettings(this, mShortcutUserType, componentId);
+        mTargets.remove(position);
+        mTargetAdapter.notifyDataSetChanged();
+    }
+
     private void onCancelButtonClicked() {
         mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH);
         mTargetAdapter.notifyDataSetChanged();
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 65cad83..07ca79a 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -373,7 +373,11 @@
                 Log.i(TAG, "Hiding image preview area. Timed out waiting for preview to load"
                         + " within " + mImageLoadTimeoutMillis + "ms.");
                 collapseParentView();
-                hideContentPreview();
+                if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+                    hideStickyContentPreview();
+                } else if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) {
+                    mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().hideContentPreview();
+                }
                 mHideParentOnFail = false;
             }
         }
@@ -829,7 +833,12 @@
 
     @Override
     protected boolean postRebuildList(boolean rebuildCompleted) {
-        updateContentPreview();
+        updateStickyContentPreview();
+        if (shouldShowStickyContentPreview()
+                || mChooserMultiProfilePagerAdapter
+                        .getCurrentRootAdapter().getContentPreviewRowCount() != 0) {
+            logActionShareWithPreview();
+        }
         return postRebuildListInternal(rebuildCompleted);
     }
 
@@ -978,6 +987,8 @@
         updateLayoutWidth(R.id.content_preview_text_layout, width, parent);
         updateLayoutWidth(R.id.content_preview_title_layout, width, parent);
         updateLayoutWidth(R.id.content_preview_file_layout, width, parent);
+        findViewById(R.id.content_preview_container)
+                .setVisibility(shouldShowStickyContentPreview() ? View.VISIBLE : View.GONE);
     }
 
     private void updateLayoutWidth(int layoutResourceId, int width, View parent) {
@@ -2413,14 +2424,14 @@
 
                 // still zero? then use a default height and leave, which
                 // can happen when there are no targets to show
-                if (rowsToShow == 0 && !shouldShowContentPreview()) {
+                if (rowsToShow == 0 && !shouldShowStickyContentPreview()) {
                     offset += getResources().getDimensionPixelSize(
                             R.dimen.chooser_max_collapsed_height);
                     mResolverDrawerLayout.setCollapsibleHeightReserved(offset);
                     return;
                 }
 
-                if (shouldShowContentPreview()) {
+                if (shouldShowStickyContentPreview()) {
                     offset += findViewById(R.id.content_preview_container).getHeight();
                 }
 
@@ -2552,7 +2563,7 @@
     }
 
     private void setupScrollListener() {
-        if (mResolverDrawerLayout == null) {
+        if (mResolverDrawerLayout == null || (hasWorkProfile() && ENABLE_TABBED_VIEW)) {
             return;
         }
         final View chooserHeader = mResolverDrawerLayout.findViewById(R.id.chooser_header);
@@ -2597,28 +2608,36 @@
         return false;
     }
 
-    private boolean shouldShowContentPreview() {
-        return mMultiProfilePagerAdapter.getActiveListAdapter().getCount() > 0
-                && isSendAction(getTargetIntent());
+    /**
+     * The sticky content preview is shown only when we have a tabbed view. It's shown above
+     * the tabs so it is not part of the scrollable list. If we are not in tabbed view,
+     * we instead show the content preview as a regular list item.
+     */
+    private boolean shouldShowStickyContentPreview() {
+        return hasWorkProfile()
+                && ENABLE_TABBED_VIEW
+                && mMultiProfilePagerAdapter.getListAdapterForUserHandle(
+                        UserHandle.of(UserHandle.myUserId())).getCount() > 0
+                && isSendAction(getTargetIntent())
+                && getResources().getBoolean(R.bool.sharesheet_show_content_preview);
     }
 
-    private void updateContentPreview() {
-        if (shouldShowContentPreview()) {
-            showContentPreview();
+    private void updateStickyContentPreview() {
+        if (shouldShowStickyContentPreview()) {
+            showStickyContentPreview();
         } else {
-            hideContentPreview();
+            hideStickyContentPreview();
         }
     }
 
-    private void showContentPreview() {
+    private void showStickyContentPreview() {
         ViewGroup contentPreviewContainer = findViewById(R.id.content_preview_container);
         contentPreviewContainer.setVisibility(View.VISIBLE);
         ViewGroup contentPreviewView = createContentPreviewView(contentPreviewContainer);
         contentPreviewContainer.addView(contentPreviewView);
-        logActionShareWithPreview();
     }
 
-    private void hideContentPreview() {
+    private void hideStickyContentPreview() {
         ViewGroup contentPreviewContainer = findViewById(R.id.content_preview_container);
         contentPreviewContainer.removeAllViews();
         contentPreviewContainer.setVisibility(View.GONE);
@@ -2634,6 +2653,7 @@
     /**
      * Used to bind types of individual item including
      * {@link ChooserGridAdapter#VIEW_TYPE_NORMAL},
+     * {@link ChooserGridAdapter#VIEW_TYPE_CONTENT_PREVIEW},
      * {@link ChooserGridAdapter#VIEW_TYPE_PROFILE},
      * and {@link ChooserGridAdapter#VIEW_TYPE_AZ_LABEL}.
      */
@@ -2695,16 +2715,18 @@
         private int mChooserTargetWidth = 0;
         private boolean mShowAzLabelIfPoss;
 
+        private boolean mHideContentPreview = false;
         private boolean mLayoutRequested = false;
 
         private int mFooterHeight = 0;
 
         private static final int VIEW_TYPE_DIRECT_SHARE = 0;
         private static final int VIEW_TYPE_NORMAL = 1;
-        private static final int VIEW_TYPE_PROFILE = 2;
-        private static final int VIEW_TYPE_AZ_LABEL = 3;
-        private static final int VIEW_TYPE_CALLER_AND_RANK = 4;
-        private static final int VIEW_TYPE_FOOTER = 5;
+        private static final int VIEW_TYPE_CONTENT_PREVIEW = 2;
+        private static final int VIEW_TYPE_PROFILE = 3;
+        private static final int VIEW_TYPE_AZ_LABEL = 4;
+        private static final int VIEW_TYPE_CALLER_AND_RANK = 5;
+        private static final int VIEW_TYPE_FOOTER = 6;
 
         private static final int MAX_TARGETS_PER_ROW_PORTRAIT = 4;
         private static final int MAX_TARGETS_PER_ROW_LANDSCAPE = 8;
@@ -2765,6 +2787,17 @@
             return maxTargets;
         }
 
+        /**
+         * Hides the list item content preview.
+         * <p>Not to be confused with the sticky content preview which is above the
+         * personal and work tabs.
+         */
+        public void hideContentPreview() {
+            mHideContentPreview = true;
+            mLayoutRequested = true;
+            notifyDataSetChanged();
+        }
+
         public boolean consumeLayoutRequest() {
             boolean oldValue = mLayoutRequested;
             mLayoutRequested = false;
@@ -2773,7 +2806,8 @@
 
         public int getRowCount() {
             return (int) (
-                    getProfileRowCount()
+                    getContentPreviewRowCount()
+                            + getProfileRowCount()
                             + getServiceTargetRowCount()
                             + getCallerAndRankedTargetRowCount()
                             + getAzLabelRowCount()
@@ -2783,7 +2817,33 @@
             );
         }
 
+        /**
+         * Returns either {@code 0} or {@code 1} depending on whether we want to show the list item
+         * content preview. Not to be confused with the sticky content preview which is above the
+         * personal and work tabs.
+         */
+        public int getContentPreviewRowCount() {
+            // For the tabbed case we show the sticky content preview above the tabs,
+            // please refer to shouldShowStickyContentPreview
+            if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+                return 0;
+            }
+            if (!isSendAction(getTargetIntent())) {
+                return 0;
+            }
+
+            if (mHideContentPreview || mChooserListAdapter == null
+                    || mChooserListAdapter.getCount() == 0) {
+                return 0;
+            }
+
+            return 1;
+        }
+
         public int getProfileRowCount() {
+            if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+                return 0;
+            }
             return mChooserListAdapter.getOtherProfile() == null ? 0 : 1;
         }
 
@@ -2815,7 +2875,8 @@
         @Override
         public int getItemCount() {
             return (int) (
-                    getProfileRowCount()
+                    getContentPreviewRowCount()
+                            + getProfileRowCount()
                             + getServiceTargetRowCount()
                             + getCallerAndRankedTargetRowCount()
                             + getAzLabelRowCount()
@@ -2827,6 +2888,8 @@
         @Override
         public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
             switch (viewType) {
+                case VIEW_TYPE_CONTENT_PREVIEW:
+                    return new ItemViewHolder(createContentPreviewView(parent), false);
                 case VIEW_TYPE_PROFILE:
                     return new ItemViewHolder(createProfileView(parent), false);
                 case VIEW_TYPE_AZ_LABEL:
@@ -2866,7 +2929,10 @@
         public int getItemViewType(int position) {
             int count;
 
-            int countSum = (count = getProfileRowCount());
+            int countSum = (count = getContentPreviewRowCount());
+            if (count > 0 && position < countSum) return VIEW_TYPE_CONTENT_PREVIEW;
+
+            countSum += (count = getProfileRowCount());
             if (count > 0 && position < countSum) return VIEW_TYPE_PROFILE;
 
             countSum += (count = getServiceTargetRowCount());
@@ -3082,7 +3148,7 @@
         }
 
         int getListPosition(int position) {
-            position -= getProfileRowCount();
+            position -= getContentPreviewRowCount() + getProfileRowCount();
 
             final int serviceCount = mChooserListAdapter.getServiceTargetCount();
             final int serviceRows = (int) Math.ceil((float) serviceCount
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index f3b6d29..417e23f 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -38,8 +38,10 @@
             IVoiceInteractor interactor);
     boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags);
     boolean hideSessionFromSession(IBinder token);
-    int startVoiceActivity(IBinder token, in Intent intent, String resolvedType);
-    int startAssistantActivity(IBinder token, in Intent intent, String resolvedType);
+    int startVoiceActivity(IBinder token, in Intent intent, String resolvedType,
+            String callingFeatureId);
+    int startAssistantActivity(IBinder token, in Intent intent, String resolvedType,
+            String callingFeatureId);
     void setKeepAwake(IBinder token, boolean keepAwake);
     void closeSystemDialogs(IBinder token);
     void finish(IBinder token);
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 9488e4f..7a0afa2 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -26,6 +26,7 @@
 import android.app.AppGlobals;
 import android.app.admin.DevicePolicyManager;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ContentResolver;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.IPackageManager;
@@ -108,7 +109,8 @@
         }
 
         final int callingUserId = getUserId();
-        final Intent newIntent = canForward(intentReceived, targetUserId);
+        final Intent newIntent = canForward(intentReceived, getUserId(), targetUserId,
+                mInjector.getIPackageManager(), getContentResolver());
         if (newIntent != null) {
             if (Intent.ACTION_CHOOSER.equals(newIntent.getAction())) {
                 Intent innerIntent = newIntent.getParcelableExtra(Intent.EXTRA_INTENT);
@@ -191,7 +193,8 @@
      * Check whether the intent can be forwarded to target user. Return the intent used for
      * forwarding if it can be forwarded, {@code null} otherwise.
      */
-    Intent canForward(Intent incomingIntent, int targetUserId)  {
+    static Intent canForward(Intent incomingIntent, int sourceUserId, int targetUserId,
+            IPackageManager packageManager, ContentResolver contentResolver)  {
         Intent forwardIntent = new Intent(incomingIntent);
         forwardIntent.addFlags(
                 Intent.FLAG_ACTIVITY_FORWARD_RESULT | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
@@ -220,11 +223,11 @@
         if (forwardIntent.getSelector() != null) {
             intentToCheck = forwardIntent.getSelector();
         }
-        String resolvedType = intentToCheck.resolveTypeIfNeeded(getContentResolver());
+        String resolvedType = intentToCheck.resolveTypeIfNeeded(contentResolver);
         sanitizeIntent(intentToCheck);
         try {
-            if (mInjector.getIPackageManager().
-                    canForwardTo(intentToCheck, resolvedType, getUserId(), targetUserId)) {
+            if (packageManager.canForwardTo(
+                    intentToCheck, resolvedType, sourceUserId, targetUserId)) {
                 return forwardIntent;
             }
         } catch (RemoteException e) {
@@ -267,7 +270,7 @@
     /**
      * Sanitize the intent in place.
      */
-    private void sanitizeIntent(Intent intent) {
+    private static void sanitizeIntent(Intent intent) {
         // Apps should not be allowed to target a specific package/ component in the target user.
         intent.setPackage(null);
         intent.setComponent(null);
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index a934de3..022573c 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -387,6 +387,11 @@
                     | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
             rdl.setOnApplyWindowInsetsListener(this::onApplyWindowInsets);
 
+            rdl.setMaxCollapsedHeight(hasWorkProfile() && ENABLE_TABBED_VIEW
+                    ? getResources().getDimensionPixelSize(
+                            R.dimen.resolver_empty_state_height_with_tabs)
+                    : getResources().getDimensionPixelSize(R.dimen.resolver_empty_state_height));
+
             mResolverDrawerLayout = rdl;
         }
 
@@ -548,13 +553,6 @@
             applyFooterView(mSystemWindowInsets.bottom);
         }
 
-        View emptyView = findViewById(R.id.empty);
-        if (emptyView != null) {
-            emptyView.setPadding(0, 0, 0, mSystemWindowInsets.bottom
-                                 + getResources().getDimensionPixelSize(
-                                         R.dimen.chooser_edge_margin_normal) * 2);
-        }
-
         return insets.consumeSystemWindowInsets();
     }
 
@@ -941,10 +939,13 @@
     }
 
     @Override // ResolverListCommunicator
-    public void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing) {
+    public final void onPostListReady(ResolverListAdapter listAdapter, boolean doPostProcessing) {
         if (isAutolaunching() || maybeAutolaunchActivity()) {
             return;
         }
+        if (shouldShowEmptyState(listAdapter)) {
+            mMultiProfilePagerAdapter.showEmptyState(listAdapter);
+        }
         if (doPostProcessing) {
             if (mMultiProfilePagerAdapter.getCurrentUserHandle().getIdentifier()
                     == UserHandle.myUserId()) {
@@ -1497,15 +1498,17 @@
     }
 
     private void setupViewVisibilities() {
-        int count = mMultiProfilePagerAdapter.getActiveListAdapter().getUnfilteredCount();
-        boolean shouldShowEmptyState = count == 0
-                && mMultiProfilePagerAdapter.getActiveListAdapter().getPlaceholderCount() == 0;
-        //TODO(arangelov): Handle empty state
-        if (!shouldShowEmptyState) {
-            addUseDifferentAppLabelIfNecessary(mMultiProfilePagerAdapter.getActiveListAdapter());
+        ResolverListAdapter activeListAdapter = mMultiProfilePagerAdapter.getActiveListAdapter();
+        if (!shouldShowEmptyState(activeListAdapter)) {
+            addUseDifferentAppLabelIfNecessary(activeListAdapter);
         }
     }
 
+    private boolean shouldShowEmptyState(ResolverListAdapter listAdapter) {
+        int count = listAdapter.getUnfilteredCount();
+        return count == 0 && listAdapter.getPlaceholderCount() == 0;
+    }
+
     /**
      * Add a label to signify that the user can pick a different app.
      * @param adapter The adapter used to provide data to item views.
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index 2321da1..ea84090 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -607,6 +607,10 @@
                 mIntents, userHandle);
     }
 
+    protected List<Intent> getIntents() {
+        return mIntents;
+    }
+
     /**
      * Necessary methods to communicate between {@link ResolverListAdapter}
      * and {@link ResolverActivity}.
diff --git a/core/java/com/android/internal/app/ResolverViewPager.java b/core/java/com/android/internal/app/ResolverViewPager.java
index 8ec94f1..84fed9c 100644
--- a/core/java/com/android/internal/app/ResolverViewPager.java
+++ b/core/java/com/android/internal/app/ResolverViewPager.java
@@ -57,10 +57,15 @@
         widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);
         int height = getMeasuredHeight();
         if (getChildCount() > 0) {
-            View firstChild = getChildAt(0);
-            firstChild.measure(widthMeasureSpec,
+            View child = getChildAt(0);
+            child.measure(widthMeasureSpec,
                     MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
-            height = firstChild.getMeasuredHeight();
+            if (height > child.getMeasuredHeight()) {
+                height = child.getMeasuredHeight();
+            }
+        }
+        if (height < getMinimumHeight()) {
+            height = getMinimumHeight();
         }
         heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index e2985566..dfd700f 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -279,10 +279,8 @@
                 uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
                 fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
                 pkgDataInfoList);
-        // Enable tracing as soon as possible for the child process.
         if (pid == 0) {
             Zygote.disableExecuteOnly(targetSdkVersion);
-            Trace.setTracingEnabled(true, runtimeFlags);
 
             // Note that this event ends at the end of handleChildProc,
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -334,9 +332,6 @@
                 niceName, startChildZygote, instructionSet, appDataDir, isTopApp,
                 pkgDataInfoList);
 
-        // Enable tracing as soon as possible for the child process.
-        Trace.setTracingEnabled(true, runtimeFlags);
-
         // Note that this event ends at the end of handleChildProc.
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
 
@@ -393,11 +388,6 @@
                 uid, gid, gids, runtimeFlags, rlimits,
                 permittedCapabilities, effectiveCapabilities);
 
-        // Enable tracing as soon as we enter the system_server.
-        if (pid == 0) {
-            Trace.setTracingEnabled(true, runtimeFlags);
-        }
-
         // Set the Java Language thread priority to the default value for new apps.
         Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
 
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 7b6262b..ae54eb2 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -914,10 +914,6 @@
             bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
 
             bootTimingsTraceLog.traceEnd(); // ZygoteInit
-            // Disable tracing so that forked processes do not inherit stale tracing tags from
-            // Zygote.
-            Trace.setTracingEnabled(false, 0);
-
 
             Zygote.initNativeState(isPrimaryZygote);
 
diff --git a/core/java/com/android/internal/telephony/WapPushManagerParams.java b/core/java/com/android/internal/telephony/WapPushManagerParams.java
deleted file mode 100644
index eafb8f1..0000000
--- a/core/java/com/android/internal/telephony/WapPushManagerParams.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.telephony;
-
-import android.telephony.WapPushManagerConnector;
-
-/**
- * WapPushManager constant value definitions.
- * @hide
- */
-public class WapPushManagerParams {
-    /**
-     * Application type activity
-     */
-    public static final int APP_TYPE_ACTIVITY = 0;
-
-    /**
-     * Application type service
-     */
-    public static final int APP_TYPE_SERVICE = 1;
-
-    /**
-     * Process Message return value
-     * Message is handled
-     */
-    public static final int MESSAGE_HANDLED = WapPushManagerConnector.RESULT_MESSAGE_HANDLED;
-
-    /**
-     * Process Message return value
-     * Application ID or content type was not found in the application ID table
-     */
-    public static final int APP_QUERY_FAILED = WapPushManagerConnector.RESULT_APP_QUERY_FAILED;
-
-    /**
-     * Process Message return value
-     * Receiver application signature check failed
-     */
-    public static final int SIGNATURE_NO_MATCH = WapPushManagerConnector.RESULT_SIGNATURE_NO_MATCH;
-
-    /**
-     * Process Message return value
-     * Receiver application was not found
-     */
-    public static final int INVALID_RECEIVER_NAME =
-            WapPushManagerConnector.RESULT_INVALID_RECEIVER_NAME;
-
-    /**
-     * Process Message return value
-     * Unknown exception
-     */
-    public static final int EXCEPTION_CAUGHT = WapPushManagerConnector.RESULT_EXCEPTION_CAUGHT;
-
-    /**
-     * Process Message return value
-     * Need further processing after WapPushManager message processing
-     */
-    public static final int FURTHER_PROCESSING = WapPushManagerConnector.RESULT_FURTHER_PROCESSING;
-}
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index e0c3823..b117e40 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -66,6 +66,12 @@
      */
     private int mMaxCollapsedHeightSmall;
 
+    /**
+     * Whether {@code mMaxCollapsedHeightSmall} was set explicitly as a layout attribute or
+     * inferred by {@code mMaxCollapsedHeight}.
+     */
+    private final boolean mIsMaxCollapsedHeightSmallExplicit;
+
     private boolean mSmallCollapsed;
 
     /**
@@ -146,6 +152,8 @@
         mMaxCollapsedHeightSmall = a.getDimensionPixelSize(
                 R.styleable.ResolverDrawerLayout_maxCollapsedHeightSmall,
                 mMaxCollapsedHeight);
+        mIsMaxCollapsedHeightSmallExplicit =
+                a.hasValue(R.styleable.ResolverDrawerLayout_maxCollapsedHeightSmall);
         mShowAtTop = a.getBoolean(R.styleable.ResolverDrawerLayout_showAtTop, false);
         a.recycle();
 
@@ -162,6 +170,21 @@
         setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
     }
 
+    /**
+     * Dynamically set the max collapsed height. Note this also updates the small collapsed
+     * height if it wasn't specified explicitly.
+     */
+    public void setMaxCollapsedHeight(int heightInPixels) {
+        if (heightInPixels == mMaxCollapsedHeight) {
+            return;
+        }
+        mMaxCollapsedHeight = heightInPixels;
+        if (!mIsMaxCollapsedHeightSmallExplicit) {
+            mMaxCollapsedHeightSmall = mMaxCollapsedHeight;
+        }
+        requestLayout();
+    }
+
     public void setSmallCollapsed(boolean smallCollapsed) {
         mSmallCollapsed = smallCollapsed;
         requestLayout();
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index fad5b0e..c523e2d 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -89,13 +89,13 @@
         ALOGE("%s: Invalid output metadata object.", __FUNCTION__);
         return BAD_VALUE;
     }
-    CameraMetadata* nativePtr = reinterpret_cast<CameraMetadata*>(env->GetLongField(thiz,
-            fields.metadata_ptr));
+    auto nativePtr = reinterpret_cast<std::shared_ptr<CameraMetadata> *>(
+            env->GetLongField(thiz, fields.metadata_ptr));
     if (nativePtr == NULL) {
         ALOGE("%s: Invalid native pointer in java metadata object.", __FUNCTION__);
         return BAD_VALUE;
     }
-    *metadata = *nativePtr;
+    *metadata = *(nativePtr->get());
     return OK;
 }
 
@@ -171,12 +171,15 @@
 
 // Less safe access to native pointer. Does NOT throw any Java exceptions if NULL.
 static CameraMetadata* CameraMetadata_getPointerNoThrow(JNIEnv *env, jobject thiz) {
-
-    if (thiz == NULL) {
-        return NULL;
+    if (thiz == nullptr) {
+        return nullptr;
     }
-
-    return reinterpret_cast<CameraMetadata*>(env->GetLongField(thiz, fields.metadata_ptr));
+    auto metadata = reinterpret_cast<std::shared_ptr<CameraMetadata> *>(
+            env->GetLongField(thiz, fields.metadata_ptr));
+    if (metadata == nullptr) {
+        return nullptr;
+    }
+    return metadata->get();
 }
 
 // Safe access to native pointer from object. Throws if not possible to access.
@@ -205,7 +208,7 @@
 static jlong CameraMetadata_allocate(JNIEnv *env, jobject thiz) {
     ALOGV("%s", __FUNCTION__);
 
-    return reinterpret_cast<jlong>(new CameraMetadata());
+    return reinterpret_cast<jlong>(new std::shared_ptr<CameraMetadata>(new CameraMetadata()));
 }
 
 static jlong CameraMetadata_allocateCopy(JNIEnv *env, jobject thiz,
@@ -214,12 +217,12 @@
 
     CameraMetadata* otherMetadata =
             CameraMetadata_getPointerThrow(env, other, "other");
-
     // In case of exception, return
     if (otherMetadata == NULL) return NULL;
 
-    // Clone native metadata and return new pointer
-    return reinterpret_cast<jlong>(new CameraMetadata(*otherMetadata));
+    // Clone native metadata and return new pointer.
+    auto clonedMetadata = new CameraMetadata(*otherMetadata);
+    return reinterpret_cast<jlong>(new std::shared_ptr<CameraMetadata>(clonedMetadata));
 }
 
 
@@ -256,14 +259,16 @@
 static void CameraMetadata_close(JNIEnv *env, jobject thiz) {
     ALOGV("%s", __FUNCTION__);
 
-    CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(env, thiz);
-
-    if (metadata != NULL) {
-        delete metadata;
-        env->SetLongField(thiz, fields.metadata_ptr, 0);
+    if (thiz != nullptr) {
+        auto metadata = reinterpret_cast<std::shared_ptr<CameraMetadata> *>(
+                env->GetLongField(thiz, fields.metadata_ptr));
+        if (metadata != nullptr) {
+            delete metadata;
+            env->SetLongField(thiz, fields.metadata_ptr, 0);
+        }
     }
 
-    LOG_ALWAYS_FATAL_IF(CameraMetadata_getPointerNoThrow(env, thiz) != NULL,
+    LOG_ALWAYS_FATAL_IF(CameraMetadata_getPointerNoThrow(env, thiz) != nullptr,
                         "Expected the native ptr to be 0 after #close");
 }
 
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index b4590f4..87be5f6 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -147,6 +147,7 @@
 static struct {
     jfieldID    mCriteria;
     jfieldID    mAllowPrivilegedPlaybackCapture;
+    jfieldID    mVoiceCommunicationCaptureAllowed;
     // other fields unused by JNI
 } gAudioMixingRuleFields;
 
@@ -1919,6 +1920,8 @@
     jobject jRuleCriteria = env->GetObjectField(jRule, gAudioMixingRuleFields.mCriteria);
     nAudioMix->mAllowPrivilegedPlaybackCapture =
             env->GetBooleanField(jRule, gAudioMixingRuleFields.mAllowPrivilegedPlaybackCapture);
+    nAudioMix->mVoiceCommunicationCaptureAllowed =
+            env->GetBooleanField(jRule, gAudioMixingRuleFields.mVoiceCommunicationCaptureAllowed);
     env->DeleteLocalRef(jRule);
     jobjectArray jCriteria = (jobjectArray)env->CallObjectMethod(jRuleCriteria,
                                                                  gArrayListMethods.toArray);
@@ -2682,6 +2685,9 @@
     gAudioMixingRuleFields.mAllowPrivilegedPlaybackCapture =
             GetFieldIDOrDie(env, audioMixingRuleClass, "mAllowPrivilegedPlaybackCapture", "Z");
 
+    gAudioMixingRuleFields.mVoiceCommunicationCaptureAllowed =
+            GetFieldIDOrDie(env, audioMixingRuleClass, "mVoiceCommunicationCaptureAllowed", "Z");
+
     jclass audioMixMatchCriterionClass =
                 FindClassOrDie(env, "android/media/audiopolicy/AudioMixingRule$AudioMixMatchCriterion");
     gAudioMixMatchCriterionClass = MakeGlobalRefOrDie(env,audioMixMatchCriterionClass);
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index b523811..0d0dc3e 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -263,6 +263,8 @@
             which_heap = HEAP_NATIVE;
         } else if (base::StartsWith(name, "[anon:scudo:")) {
             which_heap = HEAP_NATIVE;
+        } else if (base::StartsWith(name, "[anon:GWP-ASan")) {
+            which_heap = HEAP_NATIVE;
         } else if (base::StartsWith(name, "[stack")) {
             which_heap = HEAP_STACK;
         } else if (base::StartsWith(name, "[anon:stack_and_tls:")) {
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 77d4e87..6d9e8ab 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -80,6 +80,11 @@
     }
     optional Accessibility accessibility = 2;
 
+    message AdaptiveSleep {
+        optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional AdaptiveSleep adaptive_sleep = 78;
+
     // Origins for which browsers should allow geolocation by default.
     // The value is a space-separated list of origins.
     optional SettingProto allowed_geolocation_origins = 3;
@@ -574,5 +579,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 78;
+    // Next tag = 79;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b103536..d1031f4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4047,6 +4047,15 @@
         android:protectionLevel="signature|privileged|development|appop|retailDemo" />
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
 
+    <!-- Allows a data loader to read a package's access logs. The access logs contain the
+         set of pages referenced over time.
+         <p>Declaring the permission implies intention to use the API and the user of the
+         device can grant permission through the Settings application.
+         <p>Protection level: signature|privileged|appop -->
+    <permission android:name="android.permission.LOADER_USAGE_STATS"
+        android:protectionLevel="signature|privileged|appop" />
+    <uses-permission android:name="android.permission.LOADER_USAGE_STATS" />
+
     <!-- @hide @SystemApi Allows an application to observe usage time of apps. The app can register
          for callbacks when apps reach a certain usage time limit, etc. -->
     <permission android:name="android.permission.OBSERVE_APP_USAGE"
diff --git a/core/res/res/drawable/ic_no_apps.xml b/core/res/res/drawable/ic_no_apps.xml
new file mode 100644
index 0000000..4d296bd
--- /dev/null
+++ b/core/res/res/drawable/ic_no_apps.xml
@@ -0,0 +1,4 @@
+<vector android:height="32dp" android:viewportHeight="24"
+    android:viewportWidth="24" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@color/resolver_empty_state_icon" android:fillType="evenOdd" android:pathData="M18.8123,20.0145L21.3999,22.6021L22.602,21.4L2.602,1.4L1.3999,2.6021L3.9873,5.1895C3.6248,5.552 3.3998,6.052 3.3998,6.602C3.3998,7.702 4.2998,8.602 5.3998,8.602C5.9498,8.602 6.4498,8.377 6.8123,8.0145L9.9873,11.1895C9.6248,11.552 9.3998,12.052 9.3998,12.602C9.3998,13.702 10.2998,14.602 11.3998,14.602C11.9498,14.602 12.4498,14.377 12.8123,14.0145L15.9873,17.1895C15.6248,17.552 15.3998,18.052 15.3998,18.602C15.3998,19.702 16.2998,20.602 17.3998,20.602C17.9498,20.602 18.4498,20.377 18.8123,20.0145ZM17.3998,8.602C16.2998,8.602 15.3998,7.7021 15.3998,6.602C15.3998,5.502 16.2998,4.602 17.3998,4.602C18.4998,4.602 19.3998,5.502 19.3998,6.602C19.3998,7.7021 18.4998,8.602 17.3998,8.602ZM5.3998,14.6021C6.4998,14.6021 7.3998,13.7021 7.3998,12.6021C7.3998,11.5021 6.4998,10.6021 5.3998,10.6021C4.2998,10.6021 3.3998,11.5021 3.3998,12.6021C3.3998,13.7021 4.2998,14.6021 5.3998,14.6021ZM7.3998,18.6021C7.3998,19.7021 6.4998,20.6021 5.3998,20.6021C4.2998,20.6021 3.3998,19.7021 3.3998,18.6021C3.3998,17.5021 4.2998,16.6021 5.3998,16.6021C6.4998,16.6021 7.3998,17.5021 7.3998,18.6021ZM13.3998,18.6021C13.3998,19.7021 12.4998,20.6021 11.3998,20.6021C10.2998,20.6021 9.3998,19.7021 9.3998,18.6021C9.3998,17.5021 10.2998,16.6021 11.3998,16.6021C12.4998,16.6021 13.3998,17.5021 13.3998,18.6021ZM13.3999,6.602C13.3999,7.547 12.7357,8.3444 11.8511,8.5504L9.4516,6.1509C9.6576,5.2663 10.4549,4.602 11.3999,4.602C12.4999,4.602 13.3999,5.502 13.3999,6.602ZM17.8511,14.5504C18.7357,14.3444 19.3999,13.547 19.3999,12.6021C19.3999,11.5021 18.4999,10.6021 17.3999,10.6021C16.4549,10.6021 15.6576,11.2663 15.4516,12.1509L17.8511,14.5504Z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_sharing_disabled.xml b/core/res/res/drawable/ic_sharing_disabled.xml
new file mode 100644
index 0000000..d488cdb
--- /dev/null
+++ b/core/res/res/drawable/ic_sharing_disabled.xml
@@ -0,0 +1,4 @@
+<vector android:height="32dp" android:viewportHeight="24"
+    android:viewportWidth="24" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@color/resolver_empty_state_icon" android:fillType="evenOdd" android:pathData="M19.7225,20.9245L21.2011,22.4031L22.4032,21.201L2.8022,1.6L1.6001,2.8021L8.1265,9.3284L7.64,9.612C7.1,9.112 6.39,8.802 5.6,8.802C3.94,8.802 2.6,10.142 2.6,11.802C2.6,13.462 3.94,14.802 5.6,14.802C6.39,14.802 7.1,14.492 7.64,13.992L14.69,18.112C14.64,18.332 14.6,18.562 14.6,18.802C14.6,20.462 15.94,21.802 17.6,21.802C18.43,21.802 19.18,21.467 19.7225,20.9245ZM16.8938,18.0958L18.3063,19.5083C18.125,19.6895 17.875,19.802 17.6,19.802C17.05,19.802 16.6,19.352 16.6,18.802C16.6,18.527 16.7125,18.277 16.8938,18.0958ZM15.1871,16.3891L9.3881,10.5901L8.51,11.102C8.56,11.332 8.6,11.562 8.6,11.802C8.6,12.042 8.56,12.272 8.51,12.502L15.1871,16.3891ZM15.56,6.992L12.4382,8.8119L11.1766,7.5503L14.69,5.502C14.64,5.282 14.6,5.042 14.6,4.802C14.6,3.142 15.94,1.802 17.6,1.802C19.26,1.802 20.6,3.142 20.6,4.802C20.6,6.462 19.26,7.802 17.6,7.802C16.81,7.802 16.09,7.492 15.56,6.992ZM18.6,4.802C18.6,4.252 18.15,3.802 17.6,3.802C17.05,3.802 16.6,4.252 16.6,4.802C16.6,5.352 17.05,5.802 17.6,5.802C18.15,5.802 18.6,5.352 18.6,4.802ZM5.6,12.802C5.05,12.802 4.6,12.352 4.6,11.802C4.6,11.252 5.05,10.802 5.6,10.802C6.15,10.802 6.6,11.252 6.6,11.802C6.6,12.352 6.15,12.802 5.6,12.802Z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_work_apps_off.xml b/core/res/res/drawable/ic_work_apps_off.xml
new file mode 100644
index 0000000..e91806f
--- /dev/null
+++ b/core/res/res/drawable/ic_work_apps_off.xml
@@ -0,0 +1,4 @@
+<vector android:height="32dp" android:viewportHeight="24"
+    android:viewportWidth="24" android:width="32dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="@color/resolver_empty_state_icon" android:pathData="M22,7.95c0.05,-1.11 -0.84,-2 -1.95,-1.95L16,6L16,3.95c0,-1.11 -0.84,-2 -1.95,-1.95h-4C8.94,1.95 8,2.84 8,3.95v0.32l14,14L22,7.95zM14,6h-4L10,4h4v2zM21.54,20.28l-7.56,-7.56v0.01l-1.7,-1.7h0.01L7.21,5.95 3.25,1.99 1.99,3.27 4.69,6h-0.64c-1.11,0 -1.99,0.86 -1.99,1.97l-0.01,11.02c0,1.11 0.89,2.01 2,2.01h15.64l2.05,2.02L23,21.75l-1.46,-1.47z"/>
+</vector>
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 4e8a41f..24a21eb 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -75,7 +75,8 @@
             <TabWidget
                 android:id="@android:id/tabs"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content">
+                android:layout_height="wrap_content"
+                android:visibility="gone">
             </TabWidget>
             <FrameLayout
                 android:id="@android:id/tabcontent"
@@ -89,15 +90,4 @@
         </LinearLayout>
     </TabHost>
 
-    <TextView android:id="@+id/empty"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:layout_alwaysShow="true"
-              android:background="?attr/colorBackgroundFloating"
-              android:text="@string/noApplications"
-              android:padding="@dimen/chooser_edge_margin_normal"
-              android:gravity="center"
-              android:elevation="1dp"
-              android:visibility="gone"/>
-
 </com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout/chooser_list_per_profile.xml b/core/res/res/layout/chooser_list_per_profile.xml
index 212813f..38b1b52 100644
--- a/core/res/res/layout/chooser_list_per_profile.xml
+++ b/core/res/res/layout/chooser_list_per_profile.xml
@@ -13,15 +13,21 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
-<com.android.internal.widget.RecyclerView
+<RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:layoutManager="com.android.internal.widget.GridLayoutManager"
-    android:id="@+id/resolver_list"
-    android:clipToPadding="false"
-    android:background="?attr/colorBackgroundFloating"
-    android:scrollbars="none"
-    android:elevation="1dp"
-    android:nestedScrollingEnabled="true"/>
\ No newline at end of file
+    android:layout_marginTop="8dp">
+    <com.android.internal.widget.RecyclerView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layoutManager="com.android.internal.widget.GridLayoutManager"
+        android:id="@+id/resolver_list"
+        android:clipToPadding="false"
+        android:background="?attr/colorBackgroundFloating"
+        android:scrollbars="none"
+        android:elevation="1dp"
+        android:nestedScrollingEnabled="true" />
+
+    <include layout="@layout/resolver_empty_states" />
+</RelativeLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
new file mode 100644
index 0000000..6959e9c
--- /dev/null
+++ b/core/res/res/layout/resolver_empty_states.xml
@@ -0,0 +1,58 @@
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/resolver_empty_state"
+    android:layout_width="match_parent"
+    android:layout_height="@dimen/resolver_empty_state_height"
+    android:orientation="vertical"
+    android:layout_centerInParent="true"
+    android:gravity="center"
+    android:visibility="gone"
+    android:paddingStart="24dp"
+    android:paddingEnd="24dp">
+    <ImageView
+        android:id="@+id/resolver_empty_state_icon"
+        android:layout_width="32dp"
+        android:layout_height="32dp"/>
+    <TextView
+        android:id="@+id/resolver_empty_state_title"
+        android:layout_marginTop="16dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?attr/textAppearanceMedium"
+        android:textColor="@color/resolver_empty_state_text"
+        android:textSize="18sp"/>
+    <TextView
+        android:id="@+id/resolver_empty_state_subtitle"
+        android:layout_marginTop="8dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textColor="@color/resolver_empty_state_text"
+        android:textSize="14sp"
+        android:gravity="center_horizontal" />
+    <Button
+        android:id="@+id/resolver_empty_state_button"
+        android:layout_marginTop="24dp"
+        android:text="@string/resolver_switch_on_work"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@null"
+        android:textAppearance="?attr/textAppearanceMedium"
+        android:textSize="14sp"
+        android:textColor="@color/resolver_tabs_active_color"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 757cd53..6dfe24b 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -21,7 +21,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:maxWidth="@dimen/resolver_max_width"
-    android:maxCollapsedHeight="192dp"
+    android:maxCollapsedHeight="@dimen/resolver_empty_state_height"
     android:maxCollapsedHeightSmall="56dp"
     android:id="@id/contentPanel">
 
@@ -91,7 +91,8 @@
             <TabWidget
                 android:id="@android:id/tabs"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content">
+                android:layout_height="wrap_content"
+                android:visibility="gone">
             </TabWidget>
             <FrameLayout
                 android:id="@android:id/tabcontent"
@@ -101,6 +102,7 @@
                     android:id="@+id/profile_pager"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
+                    android:minHeight="@dimen/resolver_empty_state_height"
                     android:divider="?attr/dividerVertical"
                     android:footerDividersEnabled="false"
                     android:headerDividersEnabled="false"
@@ -116,17 +118,6 @@
         android:background="?attr/colorBackgroundFloating"
         android:foreground="?attr/dividerVertical" />
 
-    <TextView android:id="@+id/empty"
-              android:layout_width="match_parent"
-              android:layout_height="wrap_content"
-              android:background="?attr/colorBackgroundFloating"
-              android:elevation="@dimen/resolver_elevation"
-              android:layout_alwaysShow="true"
-              android:text="@string/noApplications"
-              android:padding="32dp"
-              android:gravity="center"
-              android:visibility="gone" />
-
     <LinearLayout
         android:id="@+id/button_bar"
         android:visibility="gone"
diff --git a/core/res/res/layout/resolver_list_per_profile.xml b/core/res/res/layout/resolver_list_per_profile.xml
index 6d8d348..d481eff 100644
--- a/core/res/res/layout/resolver_list_per_profile.xml
+++ b/core/res/res/layout/resolver_list_per_profile.xml
@@ -14,18 +14,26 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<ListView
+<RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:id="@+id/resolver_list"
-    android:clipToPadding="false"
-    android:background="?attr/colorBackgroundFloating"
-    android:elevation="@dimen/resolver_elevation"
-    android:nestedScrollingEnabled="true"
-    android:scrollbarStyle="outsideOverlay"
-    android:scrollIndicators="top|bottom"
-    android:divider="@null"
-    android:footerDividersEnabled="false"
-    android:headerDividersEnabled="false"
-    android:dividerHeight="0dp" />
\ No newline at end of file
+    android:layout_height="match_parent"
+    android:layout_marginTop="8dp">
+    <ListView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:id="@+id/resolver_list"
+        android:clipToPadding="false"
+        android:background="?attr/colorBackgroundFloating"
+        android:elevation="@dimen/resolver_elevation"
+        android:nestedScrollingEnabled="true"
+        android:scrollbarStyle="outsideOverlay"
+        android:scrollIndicators="top|bottom"
+        android:divider="@null"
+        android:footerDividersEnabled="false"
+        android:headerDividersEnabled="false"
+        android:dividerHeight="0dp" />
+
+    <include layout="@layout/resolver_empty_states" />
+</RelativeLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index b546738..4f88017 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -183,6 +183,7 @@
                     android:id="@+id/profile_pager"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
+                    android:minHeight="@dimen/resolver_empty_state_height"
                     android:dividerHeight="1dp"
                     android:divider="?attr/dividerVertical"
                     android:footerDividersEnabled="false"
diff --git a/core/res/res/values-h480dp/bools.xml b/core/res/res/values-h480dp/bools.xml
new file mode 100644
index 0000000..65e3ae6
--- /dev/null
+++ b/core/res/res/values-h480dp/bools.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <bool name="sharesheet_show_content_preview">true</bool>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-night/colors.xml b/core/res/res/values-night/colors.xml
index 33caeb6..7f77e6c 100644
--- a/core/res/res/values-night/colors.xml
+++ b/core/res/res/values-night/colors.xml
@@ -34,4 +34,6 @@
     <color name="chooser_gradient_highlight">@color/loading_gradient_highlight_color_dark</color>
 
     <color name="resolver_tabs_active_color">#FF8AB4F8</color>
+    <color name="resolver_empty_state_text">#FFFFFF</color>
+    <color name="resolver_empty_state_icon">#FFFFFF</color>
 </resources>
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 29f9f6c..c5127dc 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -29,4 +29,5 @@
          <p>The main purpose is for OEMs to customize the rendering of the
          lockscreen, setting this to true should come with customized drawables. -->
     <bool name="use_lock_pattern_drawable">false</bool>
+    <bool name="sharesheet_show_content_preview">false</bool>
 </resources>
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 731e2ec..bdec096 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -226,4 +226,6 @@
     <color name="resolver_text_color_secondary_dark">#ffC4C6C6</color>
     <color name="resolver_tabs_active_color">#FF1A73E8</color>
     <color name="resolver_tabs_inactive_color">#FF80868B</color>
+    <color name="resolver_empty_state_text">#FF202124</color>
+    <color name="resolver_empty_state_icon">#FF5F6368</color>
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 22abedc..c44a0be 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -775,6 +775,8 @@
     <dimen name="resolver_small_margin">18dp</dimen>
     <dimen name="resolver_edge_margin">24dp</dimen>
     <dimen name="resolver_elevation">1dp</dimen>
+    <dimen name="resolver_empty_state_height">212dp</dimen>
+    <dimen name="resolver_empty_state_height_with_tabs">268dp</dimen>
 
     <dimen name="chooser_action_button_icon_size">18dp</dimen>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 93e73b0..39cd00c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5343,6 +5343,24 @@
         <xliff:g id="package_name" example="com.android.example">%1$s</xliff:g> has been put into the RESTRICTED bucket</string>
 
     <!-- ResolverActivity - profile tabs -->
+    <!-- Label for the perosnal tab of the share sheet and intent resolver [CHAR LIMIT=NONE] -->
     <string name="resolver_personal_tab">Personal</string>
+    <!-- Label for the work tab of the share sheet and intent resolver [CHAR LIMIT=NONE] -->
     <string name="resolver_work_tab">Work</string>
+    <!-- Label to indicate that the user cannot share with work apps [CHAR LIMIT=NONE] -->
+    <string name="resolver_cant_share_with_work_apps">Can\u2019t share with work apps</string>
+    <!-- Label to indicate that the user cannot share with personal apps [CHAR LIMIT=NONE] -->
+    <string name="resolver_cant_share_with_personal_apps">Can\u2019t share with personal apps</string>
+    <!-- Label to explain to the user that sharing between personal and work apps is not enabled by the IT admin [CHAR LIMIT=NONE] -->
+    <string name="resolver_cant_share_cross_profile_explanation">Your IT admin blocked sharing between personal and work apps</string>
+    <!-- Label to indicate that the user has to turn on work apps in order to access work data [CHAR LIMIT=NONE] -->
+    <string name="resolver_turn_on_work_apps">Turn on work apps</string>
+    <!-- Label to explain that the user has to turn on work apps in order to access work data [CHAR LIMIT=NONE] -->
+    <string name="resolver_turn_on_work_apps_explanation">Turn on work apps to access work apps &amp; contacts</string>
+    <!-- Label to indicate that no apps are resolved [CHAR LIMIT=NONE] -->
+    <string name="resolver_no_apps_available">No apps available</string>
+    <!-- Label to explain that no apps are resolved [CHAR LIMIT=NONE] -->
+    <string name="resolver_no_apps_available_explanation">We couldn\u2019t find any apps</string>
+    <!-- Button which switches on the disabled work profile [CHAR LIMIT=NONE] -->
+    <string name="resolver_switch_on_work">Switch on work</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7f66fd0..8581084 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1561,6 +1561,7 @@
   <java-symbol type="layout" name="notification_material_media_seekbar" />
   <java-symbol type="layout" name="resolver_list_per_profile" />
   <java-symbol type="layout" name="chooser_list_per_profile" />
+  <java-symbol type="layout" name="resolver_empty_states" />
 
   <java-symbol type="anim" name="slide_in_child_bottom" />
   <java-symbol type="anim" name="slide_in_right" />
@@ -3871,6 +3872,26 @@
   <java-symbol type="string" name="resolver_personal_tab" />
   <java-symbol type="string" name="resolver_work_tab" />
   <java-symbol type="id" name="stub" />
+  <java-symbol type="id" name="resolver_empty_state" />
+  <java-symbol type="id" name="resolver_empty_state_icon" />
+  <java-symbol type="id" name="resolver_empty_state_title" />
+  <java-symbol type="id" name="resolver_empty_state_subtitle" />
+  <java-symbol type="id" name="resolver_empty_state_button" />
+  <java-symbol type="string" name="resolver_cant_share_with_work_apps" />
+  <java-symbol type="string" name="resolver_cant_share_with_personal_apps" />
+  <java-symbol type="string" name="resolver_cant_share_cross_profile_explanation" />
+  <java-symbol type="string" name="resolver_turn_on_work_apps" />
+  <java-symbol type="string" name="resolver_turn_on_work_apps_explanation" />
+  <java-symbol type="string" name="resolver_no_apps_available" />
+  <java-symbol type="string" name="resolver_no_apps_available_explanation" />
+  <java-symbol type="string" name="resolver_no_apps_available_explanation" />
+  <java-symbol type="string" name="resolver_switch_on_work" />
+  <java-symbol type="drawable" name="ic_work_apps_off" />
+  <java-symbol type="drawable" name="ic_sharing_disabled" />
+  <java-symbol type="drawable" name="ic_no_apps" />
+  <java-symbol type="dimen" name="resolver_empty_state_height" />
+  <java-symbol type="dimen" name="resolver_empty_state_height_with_tabs" />
+  <java-symbol type="bool" name="sharesheet_show_content_preview" />
 
   <!-- Toast message for background started foreground service while-in-use permission restriction feature -->
   <java-symbol type="string" name="allow_while_in_use_permission_in_fgs" />
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 2c9dba1..24fe2a0 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -45,6 +45,7 @@
 import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.CancellationSignal;
 import android.platform.test.annotations.Presubmit;
 import android.view.SurfaceControl.Transaction;
 import android.view.WindowInsets.Type;
@@ -516,6 +517,34 @@
     }
 
     @Test
+    public void testCancellation_afterGainingControl() throws Exception {
+        InsetsSourceControl control =
+                new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
+        mController.onControlsChanged(new InsetsSourceControl[] { control });
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            WindowInsetsAnimationControlListener mockListener =
+                    mock(WindowInsetsAnimationControlListener.class);
+            CancellationSignal cancellationSignal = mController.controlWindowInsetsAnimation(
+                    statusBars(), 0 /* durationMs */,
+                    new LinearInterpolator(), mockListener);
+
+            // Ready gets deferred until next predraw
+            mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+
+            verify(mockListener).onReady(any(), anyInt());
+
+            cancellationSignal.cancel();
+            verify(mockListener).onCancelled();
+        });
+        waitUntilNextFrame();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    @Test
     public void testControlImeNotReady() {
         prepareControls();
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
@@ -580,6 +609,28 @@
         });
     }
 
+    @Test
+    public void testControlImeNotReady_cancel() {
+        prepareControls();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            WindowInsetsAnimationControlListener listener =
+                    mock(WindowInsetsAnimationControlListener.class);
+            mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener)
+                    .cancel();
+
+            verify(listener).onCancelled();
+
+            // Ready gets deferred until next predraw
+            mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
+
+            verify(listener, never()).onReady(any(), anyInt());
+
+            // Pretend that timeout is happening
+            mTestClock.fastForward(2500);
+            mTestHandler.timeAdvance();
+        });
+    }
+
     private void waitUntilNextFrame() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT,
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index f17fa39..4d01cdc 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -24,6 +24,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * This class represents the current state of the GNSS engine and is used in conjunction with
@@ -339,6 +341,33 @@
         }
     }
 
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof GnssStatus)) {
+            return false;
+        }
+
+        GnssStatus that = (GnssStatus) o;
+        return mSvCount == that.mSvCount
+                && Arrays.equals(mSvidWithFlags, that.mSvidWithFlags)
+                && Arrays.equals(mCn0DbHzs, that.mCn0DbHzs)
+                && Arrays.equals(mElevations, that.mElevations)
+                && Arrays.equals(mAzimuths, that.mAzimuths)
+                && Arrays.equals(mCarrierFrequencies, that.mCarrierFrequencies)
+                && Arrays.equals(mBasebandCn0DbHzs, that.mBasebandCn0DbHzs);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(mSvCount);
+        result = 31 * result + Arrays.hashCode(mSvidWithFlags);
+        result = 31 * result + Arrays.hashCode(mCn0DbHzs);
+        return result;
+    }
+
     /**
      * Builder class to help create new GnssStatus instances.
      */
@@ -451,8 +480,8 @@
             mCn0DbHz = cn0DbHz;
             mElevation = elevation;
             mAzimuth = azimuth;
-            mCarrierFrequency = carrierFrequency;
-            mBasebandCn0DbHz = basebandCn0DbHz;
+            mCarrierFrequency = hasCarrierFrequency ? carrierFrequency : 0;
+            mBasebandCn0DbHz = hasBasebandCn0DbHz ? basebandCn0DbHz : 0;
         }
     }
 }
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index 9131f3b..0c64564 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -36,5 +36,6 @@
     void transferToRoute(String sessionId, String routeId);
 
     void notifyControlRequestSent(String id, in Intent request);
-    void requestSetVolume(String id, int volume);
+    void setRouteVolume(String routeId, int volume);
+    void setSessionVolume(String sessionId, int volume);
 }
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 6fef468..f919dce 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -51,7 +51,8 @@
     void unregisterClient2(IMediaRouter2Client client);
     void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route,
             in Intent request);
-    void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
+    void setRouteVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
+    void setSessionVolume2(IMediaRouter2Client client, String sessionId, int volume);
 
     void requestCreateSession(IMediaRouter2Client client, in MediaRoute2Info route, int requestId,
             in @nullable Bundle sessionHints);
@@ -67,8 +68,10 @@
     void requestCreateClientSession(IMediaRouter2Manager manager, String packageName,
         in @nullable MediaRoute2Info route, int requestId);
 
-    void requestSetVolume2Manager(IMediaRouter2Manager manager,
+    void setRouteVolume2Manager(IMediaRouter2Manager manager,
             in MediaRoute2Info route, int volume);
+    void setSessionVolume2Manager(IMediaRouter2Manager manager,
+            String sessionId, int volume);
 
     List<RoutingSessionInfo> getActiveSessions(IMediaRouter2Manager manager);
     void selectClientRoute(IMediaRouter2Manager manager,
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index 20a59bba5..aac195d 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -122,16 +122,25 @@
      * @hide
      */
     //TODO: Discuss what to use for request (e.g., Intent? Request class?)
-    public abstract void onControlRequest(@NonNull String routeId, @NonNull Intent request);
+    public void onControlRequest(@NonNull String routeId, @NonNull Intent request) {}
 
     /**
-     * Called when requestSetVolume is called on a route of the provider.
+     * Called when a volume setting is requested on a route of the provider
      *
      * @param routeId the id of the route
      * @param volume the target volume
      * @see MediaRoute2Info#getVolumeMax()
      */
-    public abstract void onSetVolume(@NonNull String routeId, int volume);
+    public abstract void onSetRouteVolume(@NonNull String routeId, int volume);
+
+    /**
+     * Called when {@link MediaRouter2.RoutingController#setVolume(int)} is called on
+     * a routing session of the provider
+     *
+     * @param sessionId the id of the routing session
+     * @param volume the target volume
+     */
+    public abstract void onSetSessionVolume(@NonNull String sessionId, int volume);
 
     /**
      * Gets information of the session with the given id.
@@ -513,12 +522,21 @@
         }
 
         @Override
-        public void requestSetVolume(String routeId, int volume) {
+        public void setRouteVolume(String routeId, int volume) {
             if (!checkCallerisSystem()) {
                 return;
             }
-            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetVolume,
+            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetRouteVolume,
                     MediaRoute2ProviderService.this, routeId, volume));
         }
+
+        @Override
+        public void setSessionVolume(String sessionId, int volume) {
+            if (!checkCallerisSystem()) {
+                return;
+            }
+            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetSessionVolume,
+                    MediaRoute2ProviderService.this, sessionId, volume));
+        }
     }
 }
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 6418610..0e6ade5 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -446,7 +446,7 @@
      * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
      * @hide
      */
-    public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) {
+    public void setRouteVolume(@NonNull MediaRoute2Info route, int volume) {
         Objects.requireNonNull(route, "route must not be null");
 
         Client2 client;
@@ -455,7 +455,7 @@
         }
         if (client != null) {
             try {
-                mMediaRouterService.requestSetVolume2(client, route, volume);
+                mMediaRouterService.setRouteVolume2(client, route, volume);
             } catch (RemoteException ex) {
                 Log.e(TAG, "Unable to send control request.", ex);
             }
@@ -885,6 +885,43 @@
         }
 
         /**
+         * Gets information about how volume is handled on the session.
+         *
+         * @return {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or
+         * {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}
+         */
+        @MediaRoute2Info.PlaybackVolume
+        public int getVolumeHandling() {
+            synchronized (mControllerLock) {
+                return mSessionInfo.getVolumeHandling();
+            }
+        }
+
+        /**
+         * Gets the maximum volume of the session.
+         */
+        public int getVolumeMax() {
+            synchronized (mControllerLock) {
+                return mSessionInfo.getVolumeMax();
+            }
+        }
+
+        /**
+         * Gets the current volume of the session.
+         * <p>
+         * When it's available, it represents the volume of routing session, which is a group
+         * of selected routes. To get the volume of a route,
+         * use {@link MediaRoute2Info#getVolume()}.
+         * </p>
+         * @see MediaRoute2Info#getVolume()
+         */
+        public int getVolume() {
+            synchronized (mControllerLock) {
+                return mSessionInfo.getVolume();
+            }
+        }
+
+        /**
          * Returns true if this controller is released, false otherwise.
          * If it is released, then all other getters from this instance may return invalid values.
          * Also, any operations to this instance will be ignored once released.
@@ -1040,6 +1077,42 @@
         }
 
         /**
+         * Requests a volume change for the remote session asynchronously.
+         *
+         * @param volume The new volume value between 0 and {@link RoutingController#getVolumeMax}
+         *               (inclusive).
+         * @see #getVolume()
+         */
+        public void setVolume(int volume) {
+            if (getVolumeHandling() == MediaRoute2Info.PLAYBACK_VOLUME_FIXED) {
+                Log.w(TAG, "setVolume: the routing session has fixed volume. Ignoring.");
+                return;
+            }
+            if (volume < 0 || volume > getVolumeMax()) {
+                Log.w(TAG, "setVolume: the target volume is out of range. Ignoring");
+                return;
+            }
+
+            synchronized (mControllerLock) {
+                if (mIsReleased) {
+                    Log.w(TAG, "setVolume is called on released controller. Ignoring.");
+                    return;
+                }
+            }
+            Client2 client;
+            synchronized (sRouterLock) {
+                client = mClient;
+            }
+            if (client != null) {
+                try {
+                    mMediaRouterService.setSessionVolume2(client, getId(), volume);
+                } catch (RemoteException ex) {
+                    Log.e(TAG, "setVolume: Failed to deliver request.", ex);
+                }
+            }
+        }
+
+        /**
          * Release this controller and corresponding session.
          * Any operations on this controller after calling this method will be ignored.
          * The devices that are playing media will stop playing it.
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 2c1fdab..5ce291c 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -22,6 +22,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -53,6 +55,8 @@
     @GuardedBy("sLock")
     private static MediaRouter2Manager sInstance;
 
+    private final MediaSessionManager mMediaSessionManager;
+
     final String mPackageName;
 
     private Context mContext;
@@ -89,6 +93,8 @@
         mContext = context.getApplicationContext();
         mMediaRouterService = IMediaRouterService.Stub.asInterface(
                 ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE));
+        mMediaSessionManager = (MediaSessionManager) context
+                .getSystemService(Context.MEDIA_SESSION_SERVICE);
         mPackageName = mContext.getPackageName();
         mHandler = new Handler(context.getMainLooper());
     }
@@ -150,6 +156,23 @@
         }
     }
 
+    /**
+     * Gets a {@link android.media.session.MediaController} associated with the
+     * given routing session.
+     * If there is no matching media session, {@code null} is returned.
+     */
+    @Nullable
+    public MediaController getMediaControllerForRoutingSession(
+            @NonNull RoutingSessionInfo sessionInfo) {
+        for (MediaController controller : mMediaSessionManager.getActiveSessions(null)) {
+            String volumeControlId = controller.getPlaybackInfo().getVolumeControlId();
+            if (TextUtils.equals(sessionInfo.getId(), volumeControlId)) {
+                return controller;
+            }
+        }
+        return null;
+    }
+
     //TODO: Use cache not to create array. For now, it's unclear when to purge the cache.
     //Do this when we finalize how to set control categories.
     /**
@@ -178,24 +201,24 @@
     }
 
     /**
-     * Gets routing controllers of an application with the given package name.
+     * Gets routing sessions of an application with the given package name.
      * The first element of the returned list is the system routing controller.
      *
      * @see MediaRouter2#getSystemController()
      */
     @NonNull
-    public List<RoutingController> getRoutingControllers(@NonNull String packageName) {
+    public List<RoutingSessionInfo> getRoutingSessions(@NonNull String packageName) {
         Objects.requireNonNull(packageName, "packageName must not be null");
 
-        List<RoutingController> controllers = new ArrayList<>();
+        List<RoutingSessionInfo> sessions = new ArrayList<>();
 
         for (RoutingSessionInfo sessionInfo : getActiveSessions()) {
             if (sessionInfo.isSystemSession()
                     || TextUtils.equals(sessionInfo.getClientPackageName(), packageName)) {
-                controllers.add(new RoutingController(sessionInfo));
+                sessions.add(sessionInfo);
             }
         }
-        return controllers;
+        return sessions;
     }
 
     /**
@@ -250,13 +273,12 @@
         boolean transferred = false;
         //TODO: instead of release all controllers, add an API to specify controllers that
         // should be released (or is the system controller).
-        for (RoutingController controller : getRoutingControllers(packageName)) {
-            if (!transferred && controller.getSessionInfo().getTransferrableRoutes()
-                    .contains(route.getId())) {
-                controller.transferToRoute(route);
+        for (RoutingSessionInfo sessionInfo : getRoutingSessions(packageName)) {
+            if (!transferred && sessionInfo.getTransferrableRoutes().contains(route.getId())) {
+                new RoutingController(sessionInfo).transferToRoute(route);
                 transferred = true;
-            } else if (!controller.getSessionInfo().isSystemSession()) {
-                controller.release();
+            } else if (!sessionInfo.isSystemSession()) {
+                new RoutingController(sessionInfo).release();
             }
         }
 
@@ -282,22 +304,72 @@
 
     /**
      * Requests a volume change for a route asynchronously.
+     */
+    //TODO: remove this.
+    public void requestSetVolume(MediaRoute2Info route, int volume) {
+        setRouteVolume(route, volume);
+    }
+
+    /**
+     * Requests a volume change for a route asynchronously.
      * <p>
      * It may have no effect if the route is currently not selected.
      * </p>
      *
-     * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
+     * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}
+     *               (inclusive).
      */
-    public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) {
+    public void setRouteVolume(@NonNull MediaRoute2Info route, int volume) {
         Objects.requireNonNull(route, "route must not be null");
 
+        if (route.getVolumeHandling() == MediaRoute2Info.PLAYBACK_VOLUME_FIXED) {
+            Log.w(TAG, "setRouteVolume: the route has fixed volume. Ignoring.");
+            return;
+        }
+        if (volume < 0 || volume > route.getVolumeMax()) {
+            Log.w(TAG, "setRouteVolume: the target volume is out of range. Ignoring");
+            return;
+        }
+
         Client client;
         synchronized (sLock) {
             client = mClient;
         }
         if (client != null) {
             try {
-                mMediaRouterService.requestSetVolume2Manager(client, route, volume);
+                mMediaRouterService.setRouteVolume2Manager(client, route, volume);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Unable to send control request.", ex);
+            }
+        }
+    }
+
+    /**
+     * Requests a volume change for a routing session asynchronously.
+     *
+     * @param volume The new volume value between 0 and {@link RoutingSessionInfo#getVolumeMax}
+     *               (inclusive).
+     */
+    public void setSessionVolume(@NonNull RoutingSessionInfo sessionInfo, int volume) {
+        Objects.requireNonNull(sessionInfo, "sessionInfo must not be null");
+
+        if (sessionInfo.getVolumeHandling() == MediaRoute2Info.PLAYBACK_VOLUME_FIXED) {
+            Log.w(TAG, "setSessionVolume: the route has fixed volume. Ignoring.");
+            return;
+        }
+        if (volume < 0 || volume > sessionInfo.getVolumeMax()) {
+            Log.w(TAG, "setSessionVolume: the target volume is out of range. Ignoring");
+            return;
+        }
+
+        Client client;
+        synchronized (sLock) {
+            client = mClient;
+        }
+        if (client != null) {
+            try {
+                mMediaRouterService.setSessionVolume2Manager(
+                        client, sessionInfo.getId(), volume);
             } catch (RemoteException ex) {
                 Log.e(TAG, "Unable to send control request.", ex);
             }
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index 5383ea2..0d4e666 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -56,6 +56,11 @@
     final List<String> mSelectableRoutes;
     final List<String> mDeselectableRoutes;
     final List<String> mTransferrableRoutes;
+
+    final int mVolumeHandling;
+    final int mVolumeMax;
+    final int mVolume;
+
     @Nullable
     final Bundle mControlHints;
     final boolean mIsSystemSession;
@@ -77,6 +82,10 @@
         mTransferrableRoutes = Collections.unmodifiableList(
                 convertToUniqueRouteIds(builder.mTransferrableRoutes));
 
+        mVolumeHandling = builder.mVolumeHandling;
+        mVolumeMax = builder.mVolumeMax;
+        mVolume = builder.mVolume;
+
         mControlHints = builder.mControlHints;
         mIsSystemSession = builder.mIsSystemSession;
     }
@@ -93,6 +102,10 @@
         mDeselectableRoutes = ensureList(src.createStringArrayList());
         mTransferrableRoutes = ensureList(src.createStringArrayList());
 
+        mVolumeHandling = src.readInt();
+        mVolumeMax = src.readInt();
+        mVolume = src.readInt();
+
         mControlHints = src.readBundle();
         mIsSystemSession = src.readBoolean();
     }
@@ -188,6 +201,36 @@
     }
 
     /**
+     * Gets information about how volume is handled on the session.
+     *
+     * @return {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or
+     * {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}.
+     */
+    @MediaRoute2Info.PlaybackVolume
+    public int getVolumeHandling() {
+        return mVolumeHandling;
+    }
+
+    /**
+     * Gets the maximum volume of the session.
+     */
+    public int getVolumeMax() {
+        return mVolumeMax;
+    }
+
+    /**
+     * Gets the current volume of the session.
+     * <p>
+     * When it's available, it represents the volume of routing session, which is a group
+     * of selected routes. To get the volume of each route, use {@link MediaRoute2Info#getVolume()}.
+     * </p>
+     * @see MediaRoute2Info#getVolume()
+     */
+    public int getVolume() {
+        return mVolume;
+    }
+
+    /**
      * Gets the control hints
      */
     @Nullable
@@ -218,6 +261,9 @@
         dest.writeStringList(mSelectableRoutes);
         dest.writeStringList(mDeselectableRoutes);
         dest.writeStringList(mTransferrableRoutes);
+        dest.writeInt(mVolumeHandling);
+        dest.writeInt(mVolumeMax);
+        dest.writeInt(mVolume);
         dest.writeBundle(mControlHints);
         dest.writeBoolean(mIsSystemSession);
     }
@@ -238,13 +284,17 @@
                 && Objects.equals(mSelectedRoutes, other.mSelectedRoutes)
                 && Objects.equals(mSelectableRoutes, other.mSelectableRoutes)
                 && Objects.equals(mDeselectableRoutes, other.mDeselectableRoutes)
-                && Objects.equals(mTransferrableRoutes, other.mTransferrableRoutes);
+                && Objects.equals(mTransferrableRoutes, other.mTransferrableRoutes)
+                && (mVolumeHandling == other.mVolumeHandling)
+                && (mVolumeMax == other.mVolumeMax)
+                && (mVolume == other.mVolume);
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mId, mClientPackageName, mProviderId,
-                mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferrableRoutes);
+                mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferrableRoutes,
+                mVolumeMax, mVolumeHandling, mVolume);
     }
 
     @Override
@@ -264,6 +314,9 @@
                 .append(", transferrableRoutes={")
                 .append(String.join(",", mTransferrableRoutes))
                 .append("}")
+                .append(", volumeHandling=").append(getVolumeHandling())
+                .append(", volumeMax=").append(getVolumeMax())
+                .append(", volume=").append(getVolume())
                 .append(" }");
         return result.toString();
     }
@@ -298,6 +351,9 @@
         final List<String> mSelectableRoutes;
         final List<String> mDeselectableRoutes;
         final List<String> mTransferrableRoutes;
+        int mVolumeHandling = MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
+        int mVolumeMax;
+        int mVolume;
         Bundle mControlHints;
         boolean mIsSystemSession;
 
@@ -346,6 +402,10 @@
             mDeselectableRoutes = new ArrayList<>(sessionInfo.mDeselectableRoutes);
             mTransferrableRoutes = new ArrayList<>(sessionInfo.mTransferrableRoutes);
 
+            mVolumeHandling = sessionInfo.mVolumeHandling;
+            mVolumeMax = sessionInfo.mVolumeMax;
+            mVolume = sessionInfo.mVolume;
+
             mControlHints = sessionInfo.mControlHints;
             mIsSystemSession = sessionInfo.mIsSystemSession;
         }
@@ -497,6 +557,36 @@
         }
 
         /**
+         * Sets the session's volume handling.
+         * {@link MediaRoute2Info#PLAYBACK_VOLUME_FIXED} or
+         * {@link MediaRoute2Info#PLAYBACK_VOLUME_VARIABLE}.
+         */
+        @NonNull
+        public RoutingSessionInfo.Builder setVolumeHandling(
+                @MediaRoute2Info.PlaybackVolume int volumeHandling) {
+            mVolumeHandling = volumeHandling;
+            return this;
+        }
+
+        /**
+         * Sets the session's maximum volume, or 0 if unknown.
+         */
+        @NonNull
+        public RoutingSessionInfo.Builder setVolumeMax(int volumeMax) {
+            mVolumeMax = volumeMax;
+            return this;
+        }
+
+        /**
+         * Sets the session's current volume, or 0 if unknown.
+         */
+        @NonNull
+        public RoutingSessionInfo.Builder setVolume(int volume) {
+            mVolume = volume;
+            return this;
+        }
+
+        /**
          * Sets control hints.
          */
         @NonNull
diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java
index dd9877a..61113bc 100644
--- a/media/java/android/media/audiopolicy/AudioMix.java
+++ b/media/java/android/media/audiopolicy/AudioMix.java
@@ -192,6 +192,16 @@
         return mRule.isAffectingUsage(usage);
     }
 
+    /**
+      * Returns {@code true} if the rule associated with this mix contains a
+      * RULE_MATCH_ATTRIBUTE_USAGE criterion for the given usage
+      *
+      * @hide
+      */
+    public boolean containsMatchAttributeRuleForUsage(int usage) {
+        return mRule.containsMatchAttributeRuleForUsage(usage);
+    }
+
     /** @hide */
     public boolean isRoutedToDevice(int deviceType, @NonNull String deviceAddress) {
         if ((mRouteFlags & ROUTE_FLAG_RENDER) != ROUTE_FLAG_RENDER) {
diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java
index bca3fa7..68c9593 100644
--- a/media/java/android/media/audiopolicy/AudioMixingRule.java
+++ b/media/java/android/media/audiopolicy/AudioMixingRule.java
@@ -47,10 +47,12 @@
 public class AudioMixingRule {
 
     private AudioMixingRule(int mixType, ArrayList<AudioMixMatchCriterion> criteria,
-                            boolean allowPrivilegedPlaybackCapture) {
+                            boolean allowPrivilegedPlaybackCapture,
+                            boolean voiceCommunicationCaptureAllowed) {
         mCriteria = criteria;
         mTargetMixType = mixType;
         mAllowPrivilegedPlaybackCapture = allowPrivilegedPlaybackCapture;
+        mVoiceCommunicationCaptureAllowed = voiceCommunicationCaptureAllowed;
     }
 
     /**
@@ -171,6 +173,23 @@
         return false;
     }
 
+    /**
+      * Returns {@code true} if this rule contains a RULE_MATCH_ATTRIBUTE_USAGE criterion for
+      * the given usage
+      *
+      * @hide
+      */
+    boolean containsMatchAttributeRuleForUsage(int usage) {
+        for (AudioMixMatchCriterion criterion : mCriteria) {
+            if (criterion.mRule == RULE_MATCH_ATTRIBUTE_USAGE
+                    && criterion.mAttr != null
+                    && criterion.mAttr.getUsage() == usage) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private static boolean areCriteriaEquivalent(ArrayList<AudioMixMatchCriterion> cr1,
             ArrayList<AudioMixMatchCriterion> cr2) {
         if (cr1 == null || cr2 == null) return false;
@@ -188,6 +207,8 @@
     public ArrayList<AudioMixMatchCriterion> getCriteria() { return mCriteria; }
     @UnsupportedAppUsage
     private boolean mAllowPrivilegedPlaybackCapture = false;
+    @UnsupportedAppUsage
+    private boolean mVoiceCommunicationCaptureAllowed = false;
 
     /** @hide */
     public boolean allowPrivilegedPlaybackCapture() {
@@ -195,6 +216,16 @@
     }
 
     /** @hide */
+    public boolean voiceCommunicationCaptureAllowed() {
+        return mVoiceCommunicationCaptureAllowed;
+    }
+
+    /** @hide */
+    public void setVoiceCommunicationCaptureAllowed(boolean allowed) {
+        mVoiceCommunicationCaptureAllowed = allowed;
+    }
+
+    /** @hide */
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -203,12 +234,18 @@
         final AudioMixingRule that = (AudioMixingRule) o;
         return (this.mTargetMixType == that.mTargetMixType)
                 && (areCriteriaEquivalent(this.mCriteria, that.mCriteria)
-                && this.mAllowPrivilegedPlaybackCapture == that.mAllowPrivilegedPlaybackCapture);
+                && this.mAllowPrivilegedPlaybackCapture == that.mAllowPrivilegedPlaybackCapture
+                && this.mVoiceCommunicationCaptureAllowed
+                    == that.mVoiceCommunicationCaptureAllowed);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mTargetMixType, mCriteria, mAllowPrivilegedPlaybackCapture);
+        return Objects.hash(
+            mTargetMixType,
+            mCriteria,
+            mAllowPrivilegedPlaybackCapture,
+            mVoiceCommunicationCaptureAllowed);
     }
 
     private static boolean isValidSystemApiRule(int rule) {
@@ -276,6 +313,8 @@
         private ArrayList<AudioMixMatchCriterion> mCriteria;
         private int mTargetMixType = AudioMix.MIX_TYPE_INVALID;
         private boolean mAllowPrivilegedPlaybackCapture = false;
+        // This value should be set internally according to a permission check
+        private boolean mVoiceCommunicationCaptureAllowed = false;
 
         /**
          * Constructs a new Builder with no rules.
@@ -401,6 +440,23 @@
         }
 
         /**
+         * Set if the caller of the rule is able to capture voice communication output.
+         * A system app can capture voice communication output only if it is granted with the.
+         * CAPTURE_VOICE_COMMUNICATION_OUTPUT permission.
+         *
+         * Note that this method is for internal use only and should not be called by the app that
+         * creates the rule.
+         *
+         * @return the same Builder instance.
+         *
+         * @hide
+         */
+        public @NonNull Builder voiceCommunicationCaptureAllowed(boolean allowed) {
+            mVoiceCommunicationCaptureAllowed = allowed;
+            return this;
+        }
+
+        /**
          * Add or exclude a rule for the selection of which streams are mixed together.
          * Does error checking on the parameters.
          * @param rule
@@ -583,7 +639,8 @@
          * @return a new {@link AudioMixingRule} object
          */
         public AudioMixingRule build() {
-            return new AudioMixingRule(mTargetMixType, mCriteria, mAllowPrivilegedPlaybackCapture);
+            return new AudioMixingRule(mTargetMixType, mCriteria,
+                mAllowPrivilegedPlaybackCapture, mVoiceCommunicationCaptureAllowed);
         }
     }
 }
diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
index b048158..91b9bb3 100644
--- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java
+++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java
@@ -98,6 +98,8 @@
             dest.writeInt(mix.getFormat().getChannelMask());
             // write opt-out respect
             dest.writeBoolean(mix.getRule().allowPrivilegedPlaybackCapture());
+            // write voice communication capture allowed flag
+            dest.writeBoolean(mix.getRule().voiceCommunicationCaptureAllowed());
             // write mix rules
             final ArrayList<AudioMixMatchCriterion> criteria = mix.getRule().getCriteria();
             dest.writeInt(criteria.size());
@@ -128,8 +130,10 @@
             mixBuilder.setFormat(format);
 
             AudioMixingRule.Builder ruleBuilder = new AudioMixingRule.Builder();
-            // write opt-out respect
+            // read opt-out respect
             ruleBuilder.allowPrivilegedPlaybackCapture(in.readBoolean());
+            // read voice capture allowed flag
+            ruleBuilder.voiceCommunicationCaptureAllowed(in.readBoolean());
             // read mix rules
             int nbRules = in.readInt();
             for (int j = 0 ; j < nbRules ; j++) {
@@ -169,6 +173,8 @@
             textDump += Integer.toHexString(mix.getFormat().getChannelMask()).toUpperCase() + "\n";
             textDump += "  ignore playback capture opt out="
                     + mix.getRule().allowPrivilegedPlaybackCapture() + "\n";
+            textDump += "  allow voice communication capture="
+                    + mix.getRule().voiceCommunicationCaptureAllowed() + "\n";
             // write mix rules
             final ArrayList<AudioMixMatchCriterion> criteria = mix.getRule().getCriteria();
             for (AudioMixMatchCriterion criterion : criteria) {
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index f10e5eb..3ffb951 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -44,6 +44,7 @@
 import android.media.MediaRouter2.RouteCallback;
 import android.media.MediaRouter2.RoutingControllerCallback;
 import android.media.MediaRouter2Manager;
+import android.media.MediaRouter2Utils;
 import android.media.RouteDiscoveryPreference;
 import android.media.RoutingSessionInfo;
 import android.support.test.InstrumentationRegistry;
@@ -57,6 +58,7 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -225,22 +227,22 @@
             }
         });
 
-        assertEquals(1, mManager.getRoutingControllers(mPackageName).size());
+        assertEquals(1, mManager.getRoutingSessions(mPackageName).size());
 
         mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
         latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
 
-        List<MediaRouter2Manager.RoutingController> controllers =
-                mManager.getRoutingControllers(mPackageName);
+        List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
 
-        assertEquals(2, controllers.size());
+        assertEquals(2, sessions.size());
 
-        MediaRouter2Manager.RoutingController routingController = controllers.get(1);
+        MediaRouter2Manager.RoutingController routingController =
+                mManager.getControllerForSession(sessions.get(1));
         awaitOnRouteChangedManager(
                 () -> routingController.release(),
                 ROUTE_ID1,
                 route -> TextUtils.equals(route.getClientPackageName(), null));
-        assertEquals(1, mManager.getRoutingControllers(mPackageName).size());
+        assertEquals(1, mManager.getRoutingSessions(mPackageName).size());
     }
 
     /**
@@ -266,11 +268,11 @@
                 route -> TextUtils.equals(route.getClientPackageName(), mPackageName));
         assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
 
-        List<MediaRouter2Manager.RoutingController> controllers =
-                mManager.getRoutingControllers(mPackageName);
+        List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
 
-        assertEquals(2, controllers.size());
-        MediaRouter2Manager.RoutingController routingController = controllers.get(1);
+        assertEquals(2, sessions.size());
+        MediaRouter2Manager.RoutingController routingController =
+                mManager.getControllerForSession(sessions.get(1));
 
         awaitOnRouteChangedManager(
                 () -> mManager.selectRoute(mPackageName, routes.get(ROUTE_ID5_TO_TRANSFER_TO)),
@@ -284,7 +286,33 @@
     }
 
     @Test
-    public void testControlVolumeWithManager() throws Exception {
+    public void testSetSystemRouteVolume() throws Exception {
+        // ensure client
+        addManagerCallback(new MediaRouter2Manager.Callback());
+        String selectedSystemRouteId =
+                MediaRouter2Utils.getOriginalId(
+                mManager.getActiveSessions().get(0).getSelectedRoutes().get(0));
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(Collections.emptyList());
+        MediaRoute2Info volRoute = routes.get(selectedSystemRouteId);
+        assertNotNull(volRoute);
+
+        int originalVolume = volRoute.getVolume();
+        int targetVolume = originalVolume == volRoute.getVolumeMax()
+                ? originalVolume - 1 : originalVolume + 1;
+
+        awaitOnRouteChangedManager(
+                () -> mManager.setRouteVolume(volRoute, targetVolume),
+                selectedSystemRouteId,
+                (route -> route.getVolume() == targetVolume));
+
+        awaitOnRouteChangedManager(
+                () -> mManager.setRouteVolume(volRoute, originalVolume),
+                selectedSystemRouteId,
+                (route -> route.getVolume() == originalVolume));
+    }
+
+    @Test
+    public void testSetRouteVolume() throws Exception {
         Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
         MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
 
@@ -293,17 +321,81 @@
                 ? originalVolume - 1 : originalVolume + 1;
 
         awaitOnRouteChangedManager(
-                () -> mManager.requestSetVolume(volRoute, targetVolume),
+                () -> mManager.setRouteVolume(volRoute, targetVolume),
                 ROUTE_ID_VARIABLE_VOLUME,
                 (route -> route.getVolume() == targetVolume));
 
         awaitOnRouteChangedManager(
-                () -> mManager.requestSetVolume(volRoute, originalVolume),
+                () -> mManager.setRouteVolume(volRoute, originalVolume),
                 ROUTE_ID_VARIABLE_VOLUME,
                 (route -> route.getVolume() == originalVolume));
     }
 
     @Test
+    public void testSetSessionVolume() throws Exception {
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
+        addRouterCallback(new RouteCallback());
+
+        CountDownLatch onSessionCreatedLatch = new CountDownLatch(1);
+        CountDownLatch volumeChangedLatch = new CountDownLatch(2);
+
+        // create a controller
+        addManagerCallback(new MediaRouter2Manager.Callback() {
+            @Override
+            public void onSessionCreated(MediaRouter2Manager.RoutingController controller) {
+                assertNotNull(controller);
+                onSessionCreatedLatch.countDown();
+            }
+        });
+
+        mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
+        assertTrue(onSessionCreatedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+
+        List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
+        assertEquals(2, sessions.size());
+
+        // test setSessionVolume
+        RoutingSessionInfo sessionInfo = sessions.get(1);
+        int currentVolume = sessionInfo.getVolume();
+        int targetVolume = (currentVolume == 0) ? 1 : (currentVolume - 1);
+
+        RoutingControllerCallback routingControllerCallback = new RoutingControllerCallback() {
+            @Override
+            public void onControllerUpdated(MediaRouter2.RoutingController controller) {
+                if (!TextUtils.equals(sessionInfo.getId(), controller.getId())) {
+                    return;
+                }
+                if (controller.getVolume() == targetVolume) {
+                    volumeChangedLatch.countDown();
+                }
+            }
+        };
+        mRouter2.registerControllerCallback(mExecutor, routingControllerCallback);
+
+        addManagerCallback(new MediaRouter2Manager.Callback() {
+            @Override
+            public void onSessionsUpdated() {
+                List<RoutingSessionInfo> sessions = mManager.getRoutingSessions(mPackageName);
+                if (sessions.size() != 2) {
+                    return;
+                }
+
+                if (sessions.get(1).getVolume() == targetVolume) {
+                    volumeChangedLatch.countDown();
+                }
+            }
+        });
+
+        mManager.setSessionVolume(sessionInfo, targetVolume);
+
+        try {
+            assertTrue(volumeChangedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        } finally {
+            mRouter2.unregisterControllerCallback(routingControllerCallback);
+        }
+    }
+
+    @Test
     public void testVolumeHandling() throws Exception {
         Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL);
 
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
index 1a866ca..267927f 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
@@ -18,6 +18,7 @@
 
 import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_SPEAKER;
 import static android.media.MediaRoute2Info.DEVICE_TYPE_REMOTE_TV;
+import static android.media.MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE;
 
 import android.annotation.Nullable;
 import android.content.Intent;
@@ -51,6 +52,8 @@
     public static final String ROUTE_NAME_SPECIAL_FEATURE = "Special Feature Route";
 
     public static final int VOLUME_MAX = 100;
+    public static final int SESSION_VOLUME_MAX = 50;
+    public static final int SESSION_VOLUME_INITIAL = 20;
     public static final String ROUTE_ID_FIXED_VOLUME = "route_fixed_volume";
     public static final String ROUTE_NAME_FIXED_VOLUME = "Fixed Volume Route";
     public static final String ROUTE_ID_VARIABLE_VOLUME = "route_variable_volume";
@@ -141,12 +144,12 @@
     }
 
     @Override
-    public void onSetVolume(String routeId, int volume) {
+    public void onSetRouteVolume(String routeId, int volume) {
         MediaRoute2Info route = mRoutes.get(routeId);
         if (route == null) {
             return;
         }
-        volume = Math.min(volume, Math.max(0, route.getVolumeMax()));
+        volume = Math.max(0, Math.min(volume, route.getVolumeMax()));
         mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
                 .setVolume(volume)
                 .build());
@@ -154,6 +157,19 @@
     }
 
     @Override
+    public void onSetSessionVolume(String sessionId, int volume) {
+        RoutingSessionInfo sessionInfo = getSessionInfo(sessionId);
+        if (sessionInfo == null) {
+            return;
+        }
+        volume = Math.max(0, Math.min(volume, sessionInfo.getVolumeMax()));
+        RoutingSessionInfo newSessionInfo = new RoutingSessionInfo.Builder(sessionInfo)
+                .setVolume(volume)
+                .build();
+        notifySessionUpdated(newSessionInfo);
+    }
+
+    @Override
     public void onCreateSession(String packageName, String routeId, long requestId,
             @Nullable Bundle sessionHints) {
         MediaRoute2Info route = mRoutes.get(routeId);
@@ -176,6 +192,9 @@
                 .addSelectedRoute(routeId)
                 .addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
                 .addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO)
+                .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
+                .setVolumeMax(SESSION_VOLUME_MAX)
+                .setVolume(SESSION_VOLUME_INITIAL)
                 // Set control hints with given sessionHints
                 .setControlHints(sessionHints)
                 .build();
diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index 2e4d214..d1946b0 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -28,6 +28,7 @@
 #include <utils/Color.h>
 
 #include <fcntl.h>
+#include <limits>
 #include <optional>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -70,6 +71,14 @@
         return ResultToErrorCode(result);
     }
 
+    // AImageDecoderHeaderInfo_getWidth/Height return an int32_t. Ensure that
+    // the conversion is safe.
+    const auto& info = androidCodec->getInfo();
+    if (info.width() > std::numeric_limits<int32_t>::max()
+        || info.height() > std::numeric_limits<int32_t>::max()) {
+        return ANDROID_IMAGE_DECODER_INVALID_INPUT;
+    }
+
     *outDecoder = reinterpret_cast<AImageDecoder*>(new ImageDecoder(std::move(androidCodec)));
     return ANDROID_IMAGE_DECODER_SUCCESS;
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index de4817c..2431381 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -37,6 +37,7 @@
         Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
         Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
         Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+        Settings.Secure.ADAPTIVE_SLEEP,
         Settings.Secure.AUTOFILL_SERVICE,
         Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
         Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 3f5b0da..c4330e1 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -46,7 +46,7 @@
         Settings.System.SCREEN_BRIGHTNESS_MODE,
         Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
         Settings.System.SCREEN_BRIGHTNESS_FOR_VR,
-        Settings.System.ADAPTIVE_SLEEP,
+        Settings.System.ADAPTIVE_SLEEP,             // moved to secure
         Settings.System.VIBRATE_INPUT_DEVICES,
         Settings.System.MODE_RINGER_STREAMS_AFFECTED,
         Settings.System.TEXT_AUTO_REPLACE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 849f22f..5553469 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -59,6 +59,7 @@
         VALIDATORS.put(Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(
                 Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Secure.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.AUTOFILL_SERVICE, NULLABLE_COMPONENT_NAME_VALIDATOR);
         VALIDATORS.put(
                 Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index c5d4fa9..8037266 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -126,7 +126,6 @@
         VALIDATORS.put(System.SCREEN_OFF_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.SCREEN_BRIGHTNESS_FOR_VR, new InclusiveIntegerRangeValidator(0, 255));
         VALIDATORS.put(System.SCREEN_BRIGHTNESS_MODE, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(System.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.MODE_RINGER_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.MUTE_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.VIBRATE_ON, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 66d8306..d677687 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1813,6 +1813,12 @@
                 SecureSettingsProto.Accessibility.ACCESSIBILITY_MAGNIFICATION_MODE);
         p.end(accessibilityToken);
 
+        final long adaptiveSleepToken = p.start(SecureSettingsProto.ADAPTIVE_SLEEP);
+        dumpSetting(s, p,
+                Settings.Secure.ADAPTIVE_SLEEP,
+                SecureSettingsProto.AdaptiveSleep.ENABLED);
+        p.end(adaptiveSleepToken);
+
         dumpSetting(s, p,
                 Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
                 SecureSettingsProto.ALLOWED_GEOLOCATION_ORIGINS);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7b518a6..aad46e9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3426,7 +3426,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 187;
+            private static final int SETTINGS_VERSION = 188;
 
             private final int mUserId;
 
@@ -4732,6 +4732,23 @@
                     currentVersion = 187;
                 }
 
+                if (currentVersion == 187) {
+                    // Migrate adaptive sleep setting from System to Secure.
+                    if (userId == UserHandle.USER_OWNER) {
+                        // Remove from the system settings.
+                        SettingsState systemSettings = getSystemSettingsLocked(userId);
+                        String name = Settings.System.ADAPTIVE_SLEEP;
+                        Setting setting = systemSettings.getSettingLocked(name);
+                        systemSettings.deleteSettingLocked(name);
+
+                        // Add to the secure settings.
+                        SettingsState secureSettings = getSecureSettingsLocked(userId);
+                        secureSettings.insertSettingLocked(name, setting.getValue(), null /* tag */,
+                                false /* makeDefault */, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+                    currentVersion = 188;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 0a2dd6c..bcff634 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -32,7 +32,7 @@
 public interface FalsingManager {
     int VERSION = 3;
 
-    void onSucccessfulUnlock();
+    void onSuccessfulUnlock();
 
     void onNotificationActive();
 
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index f607cc8..e586c38 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -14,6 +14,7 @@
 
 package com.android.systemui.plugins.qs;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.metrics.LogMaker;
@@ -107,6 +108,12 @@
         public int getPadding() {
             return 0;
         }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "Icon";
+        }
     }
 
     @ProvidesInterface(version = State.VERSION)
diff --git a/packages/SystemUI/res/layout/controls_detail_dialog.xml b/packages/SystemUI/res/layout/controls_detail_dialog.xml
new file mode 100644
index 0000000..f2de45a
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_detail_dialog.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/controls_activity_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />
+
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 9da7379..d596a5d 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1198,6 +1198,9 @@
     <!-- The text for the manage notifications link. [CHAR LIMIT=40] -->
     <string name="manage_notifications_text">Manage</string>
 
+    <!-- The text for the notification history link. [CHAR LIMIT=40] -->
+    <string name="manage_notifications_history_text">History</string>
+
     <!-- Section title for notifications that do not vibrate or make noise. [CHAR LIMIT=40] -->
     <string name="notification_section_header_gentle">Silent notifications</string>
 
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index e50d08c..03bc738 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -18,12 +18,14 @@
 
 import android.app.ActivityManager;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.HandlerThread;
 import android.os.Trace;
 import android.service.wallpaper.WallpaperService;
 import android.util.Log;
 import android.util.Size;
+import android.view.DisplayInfo;
 import android.view.SurfaceHolder;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -93,14 +95,20 @@
         private StatusBarStateController mController;
         private final Runnable mFinishRenderingTask = this::finishRendering;
         private final boolean mNeedTransition;
+        private boolean mShouldStopTransition;
+        @VisibleForTesting
+        final boolean mIsHighEndGfx;
+        private final boolean mDisplayNeedsBlanking;
+        private final DisplayInfo mDisplayInfo = new DisplayInfo();
         private final Object mMonitor = new Object();
         private boolean mNeedRedraw;
         // This variable can only be accessed in synchronized block.
         private boolean mWaitingForRendering;
 
         GLEngine(Context context, DozeParameters dozeParameters) {
-            mNeedTransition = ActivityManager.isHighEndGfx()
-                    && !dozeParameters.getDisplayNeedsBlanking();
+            mIsHighEndGfx = ActivityManager.isHighEndGfx();
+            mDisplayNeedsBlanking = dozeParameters.getDisplayNeedsBlanking();
+            mNeedTransition = mIsHighEndGfx && !mDisplayNeedsBlanking;
 
             // We will preserve EGL context when we are in lock screen or aod
             // to avoid janking in following transition, we need to release when back to home.
@@ -112,14 +120,23 @@
 
         @Override
         public void onCreate(SurfaceHolder surfaceHolder) {
-            mEglHelper = new EglHelper();
+            mEglHelper = getEglHelperInstance();
             // Deferred init renderer because we need to get wallpaper by display context.
-            mRenderer = new ImageWallpaperRenderer(getDisplayContext(), this /* SurfaceProxy */);
+            mRenderer = getRendererInstance();
+            getDisplayContext().getDisplay().getDisplayInfo(mDisplayInfo);
             setFixedSizeAllowed(true);
             setOffsetNotificationsEnabled(true);
             updateSurfaceSize();
         }
 
+        EglHelper getEglHelperInstance() {
+            return new EglHelper();
+        }
+
+        ImageWallpaperRenderer getRendererInstance() {
+            return new ImageWallpaperRenderer(getDisplayContext(), this /* SurfaceProxy */);
+        }
+
         private void updateSurfaceSize() {
             SurfaceHolder holder = getSurfaceHolder();
             Size frameSize = mRenderer.reportSurfaceSize();
@@ -128,6 +145,26 @@
             holder.setFixedSize(width, height);
         }
 
+        /**
+         * Check if necessary to stop transition with current wallpaper on this device. <br/>
+         * This should only be invoked after {@link #onSurfaceCreated(SurfaceHolder)}}
+         * is invoked since it needs display context and surface frame size.
+         * @return true if need to stop transition.
+         */
+        @VisibleForTesting
+        boolean checkIfShouldStopTransition() {
+            int orientation = getDisplayContext().getResources().getConfiguration().orientation;
+            Rect frame = getSurfaceHolder().getSurfaceFrame();
+            Rect display = new Rect();
+            if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+                display.set(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+            } else {
+                display.set(0, 0, mDisplayInfo.logicalHeight, mDisplayInfo.logicalWidth);
+            }
+            return mNeedTransition
+                    && (frame.width() < display.width() || frame.height() < display.height());
+        }
+
         @Override
         public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
                 float yOffsetStep, int xPixelOffset, int yPixelOffset) {
@@ -137,12 +174,14 @@
         @Override
         public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
             if (!mNeedTransition) return;
+            final long duration = mShouldStopTransition ? 0 : animationDuration;
             if (DEBUG) {
                 Log.d(TAG, "onAmbientModeChanged: inAmbient=" + inAmbientMode
-                        + ", duration=" + animationDuration);
+                        + ", duration=" + duration
+                        + ", mShouldStopTransition=" + mShouldStopTransition);
             }
             mWorker.getThreadHandler().post(
-                    () -> mRenderer.updateAmbientMode(inAmbientMode, animationDuration));
+                    () -> mRenderer.updateAmbientMode(inAmbientMode, duration));
             if (inAmbientMode && animationDuration == 0) {
                 // This means that we are transiting from home to aod, to avoid
                 // race condition between window visibility and transition,
@@ -183,6 +222,7 @@
 
         @Override
         public void onSurfaceCreated(SurfaceHolder holder) {
+            mShouldStopTransition = checkIfShouldStopTransition();
             mWorker.getThreadHandler().post(() -> {
                 mEglHelper.init(holder, needSupportWideColorGamut());
                 mRenderer.onSurfaceCreated();
@@ -348,15 +388,13 @@
         protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
             super.dump(prefix, fd, out, args);
             out.print(prefix); out.print("Engine="); out.println(this);
-
-            boolean isHighEndGfx = ActivityManager.isHighEndGfx();
-            out.print(prefix); out.print("isHighEndGfx="); out.println(isHighEndGfx);
-
+            out.print(prefix); out.print("isHighEndGfx="); out.println(mIsHighEndGfx);
             out.print(prefix); out.print("displayNeedsBlanking=");
-            out.println(
-                    mDozeParameters != null ? mDozeParameters.getDisplayNeedsBlanking() : "null");
-
+            out.println(mDisplayNeedsBlanking);
+            out.print(prefix); out.print("displayInfo="); out.print(mDisplayInfo);
             out.print(prefix); out.print("mNeedTransition="); out.println(mNeedTransition);
+            out.print(prefix); out.print("mShouldStopTransition=");
+            out.println(mShouldStopTransition);
             out.print(prefix); out.print("StatusBarState=");
             out.println(mController != null ? mController.getState() : "null");
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
index 099909d..e105795a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -35,7 +35,7 @@
     private boolean mIsReportingEnabled;
 
     @Override
-    public void onSucccessfulUnlock() {
+    public void onSuccessfulUnlock() {
 
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index d6faed5..6a64c83 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -375,7 +375,7 @@
         sessionExitpoint(false /* force */);
     }
 
-    public void onSucccessfulUnlock() {
+    public void onSuccessfulUnlock() {
         if (FalsingLog.ENABLED) {
             FalsingLog.i("onSucccessfulUnlock", "");
         }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 056ead2..0db9ffc 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -157,8 +157,8 @@
     }
 
     @Override
-    public void onSucccessfulUnlock() {
-        mInternalFalsingManager.onSucccessfulUnlock();
+    public void onSuccessfulUnlock() {
+        mInternalFalsingManager.onSuccessfulUnlock();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index 992a4cc..2f3e336 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -146,7 +146,7 @@
 
     private void updateInteractionType(@Classifier.InteractionType int type) {
         logDebug("InteractionType: " + type);
-        mClassifiers.forEach((classifier) -> classifier.setInteractionType(type));
+        mDataProvider.setInteractionType(type);
     }
 
     @Override
@@ -216,7 +216,7 @@
     }
 
     @Override
-    public void onSucccessfulUnlock() {
+    public void onSuccessfulUnlock() {
         if (mIsFalseTouchCalls != 0) {
             mMetricsLogger.histogram(FALSING_SUCCESS, mIsFalseTouchCalls);
             mIsFalseTouchCalls = 0;
@@ -263,7 +263,12 @@
     }
 
     @Override
-    public void setQsExpanded(boolean b) {
+    public void setQsExpanded(boolean expanded) {
+        if (expanded) {
+            unregisterSensors();
+        } else if (mSessionStarted) {
+            registerSensors();
+        }
     }
 
     @Override
@@ -366,10 +371,14 @@
 
     @Override
     public void onBouncerShown() {
+        unregisterSensors();
     }
 
     @Override
     public void onBouncerHidden() {
+        if (mSessionStarted) {
+            registerSensors();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
index 7555051..cf08821 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java
@@ -85,10 +85,6 @@
         return mDataProvider.getInteractionType();
     }
 
-    final void setInteractionType(@Classifier.InteractionType int interactionType) {
-        mDataProvider.setInteractionType(interactionType);
-    }
-
     /**
      * Called whenever a MotionEvent occurs.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
index 59eb00dd..eb84a8b 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent
 import android.content.Intent
+import android.provider.Settings
 import android.service.controls.actions.BooleanAction
 import android.util.Log
 import android.view.HapticFeedbackConstants
@@ -26,6 +27,8 @@
     public const val MIN_LEVEL = 0
     public const val MAX_LEVEL = 10000
 
+    private var useDetailDialog: Boolean? = null
+
     fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
         cvh.action(BooleanAction(templateId, !isChecked))
 
@@ -36,11 +39,20 @@
     fun longPress(cvh: ControlViewHolder) {
         // Long press snould only be called when there is valid control state, otherwise ignore
         cvh.cws.control?.let {
+            if (useDetailDialog == null) {
+                useDetailDialog = Settings.Secure.getInt(cvh.context.getContentResolver(),
+                    "systemui.controls_use_detail_panel", 0) != 0
+            }
+
             try {
                 cvh.layout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
-                it.getAppIntent().send()
-                val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
-                cvh.context.sendBroadcast(closeDialog)
+                if (useDetailDialog!!) {
+                    DetailDialog(cvh.context, it.getAppIntent()).show()
+                } else {
+                    it.getAppIntent().send()
+                    val closeDialog = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+                    cvh.context.sendBroadcast(closeDialog)
+                }
             } catch (e: PendingIntent.CanceledException) {
                 Log.e(ControlsUiController.TAG, "Error sending pending intent", e)
                 cvh.setTransientStatus("Error opening application")
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
new file mode 100644
index 0000000..071198b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.controls.ui
+
+import android.app.ActivityView
+import android.app.ActivityOptions
+import android.app.Dialog
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.Window
+import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+
+import com.android.systemui.R
+
+/**
+ * A dialog that provides an {@link ActivityView}, allowing the application to provide
+ * additional information and actions pertaining to a {@link android.service.controls.Control}.
+ * The activity being launched is specified by {@link android.service.controls.Control#getAppIntent}.
+ */
+class DetailDialog(
+    val parentContext: Context,
+    val intent: PendingIntent
+) : Dialog(parentContext) {
+
+    lateinit var activityView: ActivityView
+
+    val stateCallback: ActivityView.StateCallback = object : ActivityView.StateCallback() {
+        override fun onActivityViewReady(view: ActivityView) {
+            val fillInIntent = Intent()
+
+            // Apply flags to make behaviour match documentLaunchMode=always.
+            fillInIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
+            view.startActivity(intent, fillInIntent, ActivityOptions.makeBasic())
+        }
+
+        override fun onActivityViewDestroyed(view: ActivityView) {}
+
+        override fun onTaskCreated(taskId: Int, componentName: ComponentName) {}
+
+        override fun onTaskRemovalStarted(taskId: Int) {}
+    }
+
+    init {
+        val window = getWindow()
+        window.requestFeature(Window.FEATURE_NO_TITLE)
+
+        // Inflate the decor view, so the attributes below are not overwritten by the theme.
+        window.getDecorView()
+        window.getAttributes().systemUiVisibility =
+            (window.getAttributes().systemUiVisibility
+                or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
+
+        window.setLayout(MATCH_PARENT, MATCH_PARENT)
+        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+        window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+            or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+            or WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
+        window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY)
+        window.getAttributes().setFitInsetsTypes(0 /* types */)
+
+        setContentView(R.layout.controls_detail_dialog)
+
+        activityView = ActivityView(context, null, 0, false)
+        requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
+            addView(activityView)
+        }
+    }
+
+    override fun show() {
+        val attrs = getWindow().getAttributes()
+        attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+        getWindow().setAttributes(attrs)
+
+        activityView.setCallback(stateCallback)
+
+        super.show()
+    }
+
+    override fun dismiss() {
+        activityView.release()
+
+        super.dismiss()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index fa8269d..ed6675dc 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -31,7 +31,6 @@
 import android.util.MathUtils;
 import android.util.Size;
 import android.view.DisplayInfo;
-import android.view.WindowManager;
 
 import com.android.systemui.R;
 
@@ -71,8 +70,7 @@
         }
 
         DisplayInfo displayInfo = new DisplayInfo();
-        WindowManager wm = context.getSystemService(WindowManager.class);
-        wm.getDefaultDisplay().getDisplayInfo(displayInfo);
+        context.getDisplay().getDisplayInfo(displayInfo);
 
         // We only do transition in portrait currently, b/137962047.
         int orientation = context.getResources().getConfiguration().orientation;
@@ -88,6 +86,10 @@
         mImageProcessHelper = new ImageProcessHelper();
         mImageRevealHelper = new ImageRevealHelper(this);
 
+        startProcessingImage();
+    }
+
+    protected void startProcessingImage() {
         if (loadBitmap()) {
             // Compute threshold of the image, this is an async work.
             mImageProcessHelper.start(mBitmap);
@@ -113,7 +115,7 @@
         mBitmap = null;
     }
 
-    private boolean loadBitmap() {
+    protected boolean loadBitmap() {
         if (DEBUG) {
             Log.d(TAG, "loadBitmap: mBitmap=" + mBitmap);
         }
@@ -122,12 +124,7 @@
             mWcgContent = mWallpaperManager.wallpaperSupportsWcg(WallpaperManager.FLAG_SYSTEM);
             mWallpaperManager.forgetLoadedWallpaper();
             if (mBitmap != null) {
-                float scale = (float) mScissor.height() / mBitmap.getHeight();
-                int surfaceHeight = Math.max(mScissor.height(), mBitmap.getHeight());
-                int surfaceWidth = scale > 1f
-                        ? Math.round(mBitmap.getWidth() * scale)
-                        : mBitmap.getWidth();
-                mSurfaceSize.set(0, 0, surfaceWidth, surfaceHeight);
+                mSurfaceSize.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
             }
         }
         if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 9da99c4..83a6d75 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1651,7 +1651,7 @@
                             "KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
                     StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
                     handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
-                    mFalsingManager.onSucccessfulUnlock();
+                    mFalsingManager.onSuccessfulUnlock();
                     Trace.endSection();
                     break;
                 case KEYGUARD_DONE_PENDING_TIMEOUT:
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index 6498b91..ae380b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -93,6 +93,7 @@
             return mIatm.startActivityAsUser(
                     mContext.getIApplicationThread() /*caller*/,
                     mContext.getBasePackageName() /*callingPackage*/,
+                    mContext.getFeatureId() /*callingFeatureId*/,
                     intent /*intent*/,
                     intent.resolveTypeIfNeeded(mContext.getContentResolver()) /*resolvedType*/,
                     null /*resultTo*/,
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index e119bef..4a7469c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -61,6 +61,18 @@
         return buffer;
     }
 
+    /** Provides a logging buffer for all logs related to Quick Settings. */
+    @Provides
+    @Singleton
+    @QSLog
+    public static LogBuffer provideQuickSettingsLogBuffer(
+            LogcatEchoTracker bufferFilter,
+            DumpController dumpController) {
+        LogBuffer buffer = new LogBuffer("QSLog", 500, 10, bufferFilter);
+        buffer.attach(dumpController);
+        return buffer;
+    }
+
     /** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
     @Provides
     @Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSLog.java
new file mode 100644
index 0000000..dd5010c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for QS-related messages. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface QSLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 84fa700..3cf0718 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -18,6 +18,7 @@
 
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.external.TileServices;
+import com.android.systemui.qs.logging.QSLogger;
 
 import java.util.Collection;
 
@@ -27,6 +28,7 @@
     void forceCollapsePanels();
     void openPanels();
     Context getContext();
+    QSLogger getQSLogger();
     Collection<QSTile> getTiles();
     void addCallback(Callback callback);
     void removeCallback(Callback callback);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index b3d96f8..53454d6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -57,6 +57,7 @@
 import com.android.systemui.qs.QSHost.Callback;
 import com.android.systemui.qs.customize.QSCustomizer;
 import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.settings.BrightnessController;
 import com.android.systemui.settings.ToggleSliderView;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -69,6 +70,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -84,6 +86,7 @@
 
     protected final Context mContext;
     protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
+    private String mCachedSpecs = "";
     protected final View mBrightnessView;
     private final H mHandler = new H();
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
@@ -101,6 +104,7 @@
     private QSDetail.Callback mCallback;
     private BrightnessController mBrightnessController;
     private DumpController mDumpController;
+    private final QSLogger mQSLogger;
     protected QSTileHost mHost;
 
     protected QSSecurityFooter mFooter;
@@ -140,23 +144,17 @@
         }
     };
 
-    public QSPanel(Context context) {
-        this(context, null);
-    }
-
-    public QSPanel(Context context, AttributeSet attrs) {
-        this(context, attrs, null);
-    }
-
-    public QSPanel(Context context, AttributeSet attrs, DumpController dumpController) {
-        this(context, attrs, dumpController, Dependency.get(BroadcastDispatcher.class));
-    }
-
     @Inject
-    public QSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
-            DumpController dumpController, BroadcastDispatcher broadcastDispatcher) {
+    public QSPanel(
+            @Named(VIEW_CONTEXT) Context context,
+            AttributeSet attrs,
+            DumpController dumpController,
+            BroadcastDispatcher broadcastDispatcher,
+            QSLogger qsLogger
+    ) {
         super(context, attrs);
         mContext = context;
+        mQSLogger = qsLogger;
 
         setOrientation(VERTICAL);
 
@@ -166,6 +164,7 @@
 
         mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
                 R.layout.qs_paged_tile_layout, this, false);
+        mQSLogger.logAllTilesChangeListening(mListening, getDumpableTag(), mCachedSpecs);
         mTileLayout.setListening(mListening);
         addView((View) mTileLayout);
 
@@ -521,6 +520,7 @@
 
     public void setExpanded(boolean expanded) {
         if (mExpanded == expanded) return;
+        mQSLogger.logPanelExpanded(expanded, getDumpableTag());
         mExpanded = expanded;
         if (!mExpanded && mTileLayout instanceof PagedTileLayout) {
             ((PagedTileLayout) mTileLayout).setCurrentItem(0, false);
@@ -547,6 +547,7 @@
         if (mListening == listening) return;
         mListening = listening;
         if (mTileLayout != null) {
+            mQSLogger.logAllTilesChangeListening(listening, getDumpableTag(), mCachedSpecs);
             mTileLayout.setListening(listening);
         }
         if (mListening) {
@@ -554,6 +555,12 @@
         }
     }
 
+    private String getTilesSpecs() {
+        return mRecords.stream()
+                .map(tileRecord ->  tileRecord.tile.getTileSpec())
+                .collect(Collectors.joining(","));
+    }
+
     public void setListening(boolean listening, boolean expanded) {
         setListening(listening && expanded);
         getFooter().setListening(listening);
@@ -611,6 +618,7 @@
             record.tile.removeCallback(record.callback);
         }
         mRecords.clear();
+        mCachedSpecs = "";
         for (QSTile tile : tiles) {
             addTile(tile, collapsedView);
         }
@@ -675,6 +683,7 @@
         r.tileView.init(r.tile);
         r.tile.refreshState();
         mRecords.add(r);
+        mCachedSpecs = getTilesSpecs();
 
         if (mTileLayout != null) {
             mTileLayout.addTile(r);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 00e09f8..73c42d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -43,6 +43,7 @@
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.qs.external.TileLifecycleManager;
 import com.android.systemui.qs.external.TileServices;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.phone.AutoTileManager;
@@ -82,6 +83,7 @@
     private final PluginManager mPluginManager;
     private final DumpController mDumpController;
     private final BroadcastDispatcher mBroadcastDispatcher;
+    private final QSLogger mQSLogger;
 
     private final List<Callback> mCallbacks = new ArrayList<>();
     private AutoTileManager mAutoTiles;
@@ -101,12 +103,14 @@
             Provider<AutoTileManager> autoTiles,
             DumpController dumpController,
             BroadcastDispatcher broadcastDispatcher,
-            Optional<StatusBar> statusBarOptional) {
+            Optional<StatusBar> statusBarOptional,
+            QSLogger qsLogger) {
         mIconController = iconController;
         mContext = context;
         mTunerService = tunerService;
         mPluginManager = pluginManager;
         mDumpController = dumpController;
+        mQSLogger = qsLogger;
         mBroadcastDispatcher = broadcastDispatcher;
 
         mServices = new TileServices(this, bgLooper, mBroadcastDispatcher);
@@ -159,6 +163,10 @@
         onTuningChanged(TILES_SETTING, value);
     }
 
+    public QSLogger getQSLogger() {
+        return mQSLogger;
+    }
+
     @Override
     public void addCallback(Callback callback) {
         mCallbacks.add(callback);
@@ -223,6 +231,7 @@
         mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
                 tile -> {
                     Log.d(TAG, "Destroying tile: " + tile.getKey());
+                    mQSLogger.logTileDestroyed(tile.getKey(), "Tile removed");
                     tile.getValue().destroy();
                 });
         final LinkedHashMap<String, QSTile> newTiles = new LinkedHashMap<>();
@@ -237,9 +246,11 @@
                         tile.userSwitch(currentUser);
                     }
                     newTiles.put(tileSpec, tile);
+                    mQSLogger.logTileAdded(tileSpec);
                 } else {
                     tile.destroy();
                     Log.d(TAG, "Destroying not available tile: " + tileSpec);
+                    mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
                 }
             } else {
                 Log.d(TAG, "Creating tile: " + tileSpec);
@@ -249,9 +260,11 @@
                         if (tile.isAvailable()) {
                             tile.setTileSpec(tileSpec);
                             newTiles.put(tileSpec, tile);
+                            mQSLogger.logTileAdded(tileSpec);
                         } else {
                             tile.destroy();
                             Log.d(TAG, "Destroying not available tile: " + tileSpec);
+                            mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
                         }
                     }
                 } catch (Throwable t) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index e920701..fda2f6a4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -29,10 +29,12 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.DumpController;
 import com.android.systemui.R;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.SignalState;
 import com.android.systemui.plugins.qs.QSTile.State;
 import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 import com.android.systemui.util.Utils;
@@ -65,9 +67,14 @@
     private QSTileLayout mRegularTileLayout;
 
     @Inject
-    public QuickQSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
-            DumpController dumpController) {
-        super(context, attrs, dumpController);
+    public QuickQSPanel(
+            @Named(VIEW_CONTEXT) Context context,
+            AttributeSet attrs,
+            DumpController dumpController,
+            BroadcastDispatcher broadcastDispatcher,
+            QSLogger qsLogger
+    ) {
+        super(context, attrs, dumpController, broadcastDispatcher, qsLogger);
         if (mFooter != null) {
             removeView(mFooter.getView());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index c118630f..21a424c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -210,8 +210,10 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         if (mListening == listening) return;
         mListening = listening;
+
         try {
             if (listening) {
                 updateDefaultTileAndIcon();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
new file mode 100644
index 0000000..ab8de26
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.logging
+
+import android.service.quicksettings.Tile
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.LogLevel.VERBOSE
+import com.android.systemui.log.LogMessage
+import com.android.systemui.log.dagger.QSLog
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.statusbar.StatusBarState
+import javax.inject.Inject
+
+private const val TAG = "QSLog"
+
+class QSLogger @Inject constructor(
+    @QSLog private val buffer: LogBuffer
+) {
+
+    fun logTileAdded(tileSpec: String) {
+        log(DEBUG, {
+            str1 = tileSpec
+        }, {
+            "[$str1] Tile added"
+        })
+    }
+
+    fun logTileDestroyed(tileSpec: String, reason: String) {
+        log(DEBUG, {
+            str1 = tileSpec
+            str2 = reason
+        }, {
+            "[$str1] Tile destroyed. Reason: $str2"
+        })
+    }
+
+    fun logTileChangeListening(tileSpec: String, listening: Boolean) {
+        log(VERBOSE, {
+            bool1 = listening
+            str1 = tileSpec
+        }, {
+            "[$str1] Tile listening=$bool1"
+        })
+    }
+
+    fun logAllTilesChangeListening(listening: Boolean, containerName: String, allSpecs: String) {
+        log(DEBUG, {
+            bool1 = listening
+            str1 = containerName
+            str2 = allSpecs
+        }, {
+            "Tiles listening=$bool1 in $str1. $str2"
+        })
+    }
+
+    fun logTileClick(tileSpec: String, statusBarState: Int, state: Int) {
+        log(DEBUG, {
+            str1 = tileSpec
+            int1 = statusBarState
+            str2 = StatusBarState.toShortString(statusBarState)
+            str3 = toStateString(state)
+        }, {
+            "[$str1] Tile clicked. StatusBarState=$str2. TileState=$str3"
+        })
+    }
+
+    fun logTileSecondaryClick(tileSpec: String, statusBarState: Int, state: Int) {
+        log(DEBUG, {
+            str1 = tileSpec
+            int1 = statusBarState
+            str2 = StatusBarState.toShortString(statusBarState)
+            str3 = toStateString(state)
+        }, {
+            "[$str1] Tile long clicked. StatusBarState=$str2. TileState=$str3"
+        })
+    }
+
+    fun logTileLongClick(tileSpec: String, statusBarState: Int, state: Int) {
+        log(DEBUG, {
+            str1 = tileSpec
+            int1 = statusBarState
+            str2 = StatusBarState.toShortString(statusBarState)
+            str3 = toStateString(state)
+        }, {
+            "[$str1] Tile long clicked. StatusBarState=$str2. TileState=$str3"
+        })
+    }
+
+    fun logTileUpdated(tileSpec: String, state: QSTile.State) {
+        log(VERBOSE, {
+            str1 = tileSpec
+            str2 = state.label.toString()
+            str3 = state.icon.toString()
+            int1 = state.state
+            if (state is QSTile.SignalState) {
+                bool1 = true
+                bool2 = state.activityIn
+                bool3 = state.activityOut
+            }
+        }, {
+            "[$str1] Tile updated. Label=$str2. State=$int1. Icon=$str3." +
+                if (bool1) " Activity in/out=$bool2/$bool3" else ""
+        })
+    }
+
+    fun logPanelExpanded(expanded: Boolean, containerName: String) {
+        log(DEBUG, {
+            str1 = containerName
+            bool1 = expanded
+        }, {
+            "$str1 expanded=$bool1"
+        })
+    }
+
+    private fun toStateString(state: Int): String {
+        return when (state) {
+            Tile.STATE_ACTIVE -> "active"
+            Tile.STATE_INACTIVE -> "inactive"
+            Tile.STATE_UNAVAILABLE -> "unavailable"
+            else -> "wrong state"
+        }
+    }
+
+    private inline fun log(
+        logLevel: LogLevel,
+        initializer: LogMessage.() -> Unit,
+        noinline printer: LogMessage.() -> String
+    ) {
+        buffer.log(TAG, logLevel, initializer, printer)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index e559694..60f6647 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -27,6 +27,8 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_ACTION;
 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
 
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.content.Context;
 import android.content.Intent;
@@ -41,7 +43,6 @@
 import android.util.Log;
 import android.util.SparseArray;
 
-import androidx.annotation.NonNull;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
@@ -63,6 +64,7 @@
 import com.android.systemui.qs.PagedTileLayout.TilePage;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QuickStatusBarHeader;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tiles.QSSettingsControllerKt;
 import com.android.systemui.qs.tiles.QSSettingsPanel;
 
@@ -95,6 +97,7 @@
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
     private final StatusBarStateController
             mStatusBarStateController = Dependency.get(StatusBarStateController.class);
+    private final QSLogger mQSLogger;
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<>();
     private final Object mStaleListener = new Object();
@@ -156,6 +159,7 @@
         mState = newTileState();
         mTmpState = newTileState();
         mQSSettingsPanelOption = QSSettingsControllerKt.getQSSettingsPanelOption();
+        mQSLogger = host.getQSLogger();
     }
 
     protected final void resetStates() {
@@ -243,6 +247,7 @@
         mMetricsLogger.write(populate(new LogMaker(ACTION_QS_CLICK).setType(TYPE_ACTION)
                 .addTaggedData(FIELD_STATUS_BAR_STATE,
                         mStatusBarStateController.getState())));
+        mQSLogger.logTileClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
         mHandler.sendEmptyMessage(H.CLICK);
     }
 
@@ -250,6 +255,8 @@
         mMetricsLogger.write(populate(new LogMaker(ACTION_QS_SECONDARY_CLICK).setType(TYPE_ACTION)
                 .addTaggedData(FIELD_STATUS_BAR_STATE,
                         mStatusBarStateController.getState())));
+        mQSLogger.logTileSecondaryClick(mTileSpec, mStatusBarStateController.getState(),
+                mState.state);
         mHandler.sendEmptyMessage(H.SECONDARY_CLICK);
     }
 
@@ -257,6 +264,7 @@
         mMetricsLogger.write(populate(new LogMaker(ACTION_QS_LONG_PRESS).setType(TYPE_ACTION)
                 .addTaggedData(FIELD_STATUS_BAR_STATE,
                         mStatusBarStateController.getState())));
+        mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state);
         mHandler.sendEmptyMessage(H.LONG_CLICK);
 
         Prefs.putInt(
@@ -359,6 +367,7 @@
         handleUpdateState(mTmpState, arg);
         final boolean changed = mTmpState.copyTo(mState);
         if (changed) {
+            mQSLogger.logTileUpdated(mTileSpec, mState);
             handleStateChanged();
         }
         mHandler.removeMessages(H.STALE);
@@ -445,9 +454,15 @@
         mIsFullQs = 0;
     }
 
-    protected abstract void handleSetListening(boolean listening);
+    @CallSuper
+    protected void handleSetListening(boolean listening) {
+        if (mTileSpec != null) {
+            mQSLogger.logTileChangeListening(mTileSpec, listening);
+        }
+    }
 
     protected void handleDestroy() {
+        mQSLogger.logTileDestroyed(mTileSpec, "Handle destroy");
         if (mListeners.size() != 0) {
             handleSetListening(false);
         }
@@ -592,6 +607,12 @@
         public Drawable getInvisibleDrawable(Context context) {
             return mInvisibleDrawable;
         }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return "DrawableIcon";
+        }
     }
 
     public static class DrawableIconWithRes extends DrawableIcon {
@@ -606,6 +627,12 @@
         public boolean equals(Object o) {
             return o instanceof DrawableIconWithRes && ((DrawableIconWithRes) o).mId == mId;
         }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return String.format("DrawableIconWithRes[resId=0x%08x]", mId);
+        }
     }
 
     public static class ResourceIcon extends Icon {
@@ -642,6 +669,7 @@
         }
 
         @Override
+        @NonNull
         public String toString() {
             return String.format("ResourceIcon[resId=0x%08x]", mResId);
         }
@@ -660,6 +688,12 @@
             // workaround: get a clean state for every new AVD
             return context.getDrawable(mAnimatedResId).getConstantState().newDrawable();
         }
+
+        @Override
+        @NonNull
+        public String toString() {
+            return String.format("AnimationIcon[resId=0x%08x]", mResId);
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index 5e297e2..b24fdbf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -130,6 +130,7 @@
     }
 
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         if (mListening == listening) return;
         mListening = listening;
         if (listening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index d62f10d..4449d48 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -74,6 +74,7 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         mSetting.setListening(listening);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 361b6c1..cc7aaea 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -82,10 +82,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     protected void handleClick() {
         // Secondary clicks are header clicks, just toggle.
         final boolean isEnabled = mState.value;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 58de057..4b53ae2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -97,6 +97,7 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         if (DEBUG) Log.d(TAG, "handleSetListening " + listening);
         if (!listening) {
             mController.setDiscovering(false);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 436869d..bc03ca6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -91,10 +91,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public Intent getLongClickIntent() {
         if (getState().state == Tile.STATE_UNAVAILABLE) {
             return new Intent(Settings.ACTION_WIRELESS_SETTINGS);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index ecb4048..9c0030d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -66,6 +66,7 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         mSetting.setListening(listening);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 79996bc..8ba6084 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -50,10 +50,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public Intent getLongClickIntent() {
         return new Intent(Settings.ACTION_DATA_SAVER_SETTINGS);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 9215da4..ebf45a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -289,6 +289,7 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         if (mListening == listening) return;
         mListening = listening;
         if (mListening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 792c364..27ccd7c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -58,10 +58,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     protected void handleUserSwitch(int newUserId) {
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 91b3ae4..0c86157 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -69,6 +69,7 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         if (mListening == listening) return;
         mListening = listening;
         if (listening) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index e617867..02f364b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -61,10 +61,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public Intent getLongClickIntent() {
         return new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index f0140ba..4bee075 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -57,6 +57,7 @@
 
     @Override
     public void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         mListening = listening;
         if (mListening) {
             mBroadcastDispatcher.registerReceiver(mNfcReceiver,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 241b375..bc1c1e1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -190,6 +190,7 @@
 
     @Override
     protected void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
         mIsListening = listening;
         if (listening) {
             mListener.setCallback(this);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index 7ca1e44..2557226 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -51,9 +51,6 @@
         return new BooleanState();
     }
 
-    public void handleSetListening(boolean listening) {
-    }
-
     @Override
     public Intent getLongClickIntent() {
         return new Intent(Settings.ACTION_DISPLAY_SETTINGS);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 596c3b9..88a30a1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -105,10 +105,6 @@
     }
 
     @Override
-    protected void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public CharSequence getTileLabel() {
         return mContext.getString(R.string.quick_settings_screen_record_label);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 8f1769b..b90ca01 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -32,10 +32,11 @@
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 
-import javax.inject.Inject;
 import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
 
+import javax.inject.Inject;
+
 /**
  * Quick Settings tile for: Night Mode / Dark Theme / Dark Mode.
  *
@@ -141,10 +142,6 @@
     }
 
     @Override
-    protected void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public CharSequence getTileLabel() {
         return getState().label;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
index 7c1ffde..aab30d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserTile.java
@@ -73,10 +73,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public CharSequence getTileLabel() {
         return getState().label;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 6e8dcf3..39bfd5a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -84,10 +84,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public void setDetailListening(boolean listening) {
         if (listening) {
             mWifiController.addAccessPointCallback(mDetailAdapter);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index e54ee51..318c0c4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -50,10 +50,6 @@
     }
 
     @Override
-    public void handleSetListening(boolean listening) {
-    }
-
-    @Override
     public Intent getLongClickIntent() {
         return new Intent(Settings.ACTION_MANAGED_PROFILE_SETTINGS);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
index 3fa1954..b5c81b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java
@@ -151,10 +151,6 @@
                         .append(" ");
             }
 
-            if (notifEntry.hasInflationError()) {
-                rksb.append("(!)hasInflationError ");
-            }
-
             if (notifEntry.getDismissState() != NOT_DISMISSED) {
                 rksb.append("dismissState=")
                         .append(notifEntry.getDismissState())
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
index 9272e51b..83f56cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -18,9 +18,7 @@
 
 import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
 
-import android.os.RemoteException;
 import android.service.notification.NotificationStats;
-import android.service.notification.StatusBarNotification;
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
@@ -29,6 +27,7 @@
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
 import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
 
 import javax.inject.Inject;
@@ -44,6 +43,7 @@
 
     private final IStatusBarService mStatusBarService;
     private final NotifCollection mNotifCollection;
+    private final NotifInflationErrorManager mNotifErrorManager;
 
     private NotificationRowBinderImpl mNotificationRowBinder;
     private InflationCallback mExternalInflationCallback;
@@ -51,9 +51,11 @@
     @Inject
     public NotifInflaterImpl(
             IStatusBarService statusBarService,
-            NotifCollection notifCollection) {
+            NotifCollection notifCollection,
+            NotifInflationErrorManager errorManager) {
         mStatusBarService = statusBarService;
         mNotifCollection = notifCollection;
+        mNotifErrorManager = errorManager;
     }
 
     /**
@@ -81,7 +83,6 @@
     @Override
     public void inflateViews(NotificationEntry entry) {
         try {
-            entry.setHasInflationError(false);
             requireBinder().inflateViews(entry, getDismissCallback(entry));
         } catch (InflationException e) {
             // logged in mInflationCallback.handleInflationException
@@ -131,25 +132,12 @@
                 public void handleInflationException(
                         NotificationEntry entry,
                         Exception e) {
-                    entry.setHasInflationError(true);
-                    try {
-                        final StatusBarNotification sbn = entry.getSbn();
-                        // report notification inflation errors back up
-                        // to notification delegates
-                        mStatusBarService.onNotificationError(
-                                sbn.getPackageName(),
-                                sbn.getTag(),
-                                sbn.getId(),
-                                sbn.getUid(),
-                                sbn.getInitialPid(),
-                                e.getMessage(),
-                                sbn.getUserId());
-                    } catch (RemoteException ex) {
-                    }
+                    mNotifErrorManager.setInflationError(entry, e);
                 }
 
                 @Override
                 public void onAsyncInflationFinished(NotificationEntry entry) {
+                    mNotifErrorManager.clearInflationError(entry);
                     if (mExternalInflationCallback != null) {
                         mExternalInflationCallback.onInflationFinished(entry);
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 006d40d..f482d37 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -122,9 +122,6 @@
      */
     @CancellationReason int mCancellationReason = REASON_NOT_CANCELED;
 
-    /** @see #hasInflationError() */
-    private boolean mHasInflationError;
-
     /** @see #getDismissState() */
     @NonNull private DismissState mDismissState = DismissState.NOT_DISMISSED;
 
@@ -274,23 +271,6 @@
      */
 
     /**
-     * Whether this notification had an error when attempting to inflate. This is only used in
-     * the NewNotifPipeline
-     */
-    public boolean hasInflationError() {
-        return mHasInflationError;
-    }
-
-    /**
-     * Set whether the notification has an error while inflating.
-     *
-     * TODO: Move this into an inflation error manager class.
-     */
-    public void setHasInflationError(boolean hasError) {
-        mHasInflationError = hasError;
-    }
-
-    /**
      * Set if the user has dismissed this notif but we haven't yet heard back from system server to
      * confirm the dismissal.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 1e5946a..1c8fdac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -16,12 +16,18 @@
 
 package com.android.systemui.statusbar.notification.collection.coordinator;
 
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -34,7 +40,7 @@
  * Aborts inflation when a notification is removed.
  *
  * If a notification is not done inflating, this coordinator will filter the notification out
- * from the NotifListBuilder.
+ * from the {@link ShadeListBuilder}.
  */
 @Singleton
 public class PreparationCoordinator implements Coordinator {
@@ -42,15 +48,22 @@
 
     private final PreparationCoordinatorLogger mLogger;
     private final NotifInflater mNotifInflater;
+    private final NotifInflationErrorManager mNotifErrorManager;
     private final List<NotificationEntry> mPendingNotifications = new ArrayList<>();
+    private final IStatusBarService mStatusBarService;
 
     @Inject
     public PreparationCoordinator(
             PreparationCoordinatorLogger logger,
-            NotifInflaterImpl notifInflater) {
+            NotifInflaterImpl notifInflater,
+            NotifInflationErrorManager errorManager,
+            IStatusBarService service) {
         mLogger = logger;
         mNotifInflater = notifInflater;
         mNotifInflater.setInflationCallback(mInflationCallback);
+        mNotifErrorManager = errorManager;
+        mNotifErrorManager.addInflationErrorListener(mInflationErrorListener);
+        mStatusBarService = service;
     }
 
     @Override
@@ -84,8 +97,7 @@
          */
         @Override
         public boolean shouldFilterOut(NotificationEntry entry, long now) {
-            if (entry.hasInflationError()) {
-                mPendingNotifications.remove(entry);
+            if (mNotifErrorManager.hasInflationError(entry)) {
                 return true;
             }
             return false;
@@ -112,6 +124,34 @@
         }
     };
 
+    private final NotifInflationErrorManager.NotifInflationErrorListener mInflationErrorListener =
+            new NotifInflationErrorManager.NotifInflationErrorListener() {
+        @Override
+        public void onNotifInflationError(NotificationEntry entry, Exception e) {
+            mPendingNotifications.remove(entry);
+            try {
+                final StatusBarNotification sbn = entry.getSbn();
+                // report notification inflation errors back up
+                // to notification delegates
+                mStatusBarService.onNotificationError(
+                        sbn.getPackageName(),
+                        sbn.getTag(),
+                        sbn.getId(),
+                        sbn.getUid(),
+                        sbn.getInitialPid(),
+                        e.getMessage(),
+                        sbn.getUserId());
+            } catch (RemoteException ex) {
+            }
+            mNotifInflationErrorFilter.invalidateList();
+        }
+
+        @Override
+        public void onNotifInflationErrorCleared(NotificationEntry entry) {
+            mNotifInflationErrorFilter.invalidateList();
+        }
+    };
+
     private void inflateEntry(NotificationEntry entry, String reason) {
         abortInflation(entry, reason);
         mPendingNotifications.add(entry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index c2da517..41b248f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -78,9 +78,9 @@
         mDismissButton.setText(R.string.clear_all_notifications_text);
         mDismissButton.setContentDescription(
                 mContext.getString(R.string.accessibility_clear_all));
-        mManageButton.setText(R.string.manage_notifications_text);
+        mManageButton.setText(R.string.manage_notifications_history_text);
         mManageButton.setContentDescription(
-                mContext.getString(R.string.accessibility_manage_notification));
+                mContext.getString(R.string.manage_notifications_history_text));
     }
 
     public boolean isButtonVisible() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManager.java
new file mode 100644
index 0000000..a3ca084
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifInflationErrorManager.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import androidx.annotation.NonNull;
+import androidx.collection.ArraySet;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * A manager handling the error state of a notification when it encounters an exception while
+ * inflating. We don't want to show these notifications to the user but may want to keep them
+ * around for logging purposes.
+ */
+@Singleton
+public class NotifInflationErrorManager {
+
+    Set<NotificationEntry> mErroredNotifs = new ArraySet<>();
+    List<NotifInflationErrorListener> mListeners = new ArrayList<>();
+
+    @Inject
+    public NotifInflationErrorManager() { }
+
+    /**
+     * Mark the notification as errored out due to encountering an exception while inflating.
+     *
+     * @param e the exception encountered while inflating
+     */
+    public void setInflationError(NotificationEntry entry, Exception e) {
+        mErroredNotifs.add(entry);
+        for (int i = 0; i < mListeners.size(); i++) {
+            mListeners.get(i).onNotifInflationError(entry, e);
+        }
+    }
+
+    /**
+     * Notification inflated successfully and is no longer errored out.
+     */
+    public void clearInflationError(NotificationEntry entry) {
+        if (mErroredNotifs.contains(entry)) {
+            mErroredNotifs.remove(entry);
+            for (int i = 0; i < mListeners.size(); i++) {
+                mListeners.get(i).onNotifInflationErrorCleared(entry);
+            }
+        }
+    }
+
+    /**
+     * Whether or not the notification encountered an exception while inflating.
+     */
+    public boolean hasInflationError(@NonNull NotificationEntry entry) {
+        return mErroredNotifs.contains(entry);
+    }
+
+    /**
+     * Add listener for changes in inflation error state.
+     */
+    public void addInflationErrorListener(NotifInflationErrorListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Listener for changes in notification inflation error state.
+     */
+    public interface NotifInflationErrorListener {
+
+        /**
+         * Called when notification encounters an inflation exception.
+         *
+         * @param e the exception encountered while inflating
+         */
+        void onNotifInflationError(NotificationEntry entry, Exception e);
+
+        /**
+         * Called when notification inflation error is cleared.
+         */
+        default void onNotifInflationErrorCleared(NotificationEntry entry) {}
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
index f124179..f783245 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStage.java
@@ -18,12 +18,8 @@
 
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL;
 
-import android.os.RemoteException;
-import android.service.notification.StatusBarNotification;
-
 import androidx.annotation.NonNull;
 
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
@@ -41,14 +37,14 @@
 @Singleton
 public class RowContentBindStage extends BindStage<RowContentBindParams> {
     private final NotificationRowContentBinder mBinder;
-    private final IStatusBarService mStatusBarService;
+    private final NotifInflationErrorManager mNotifInflationErrorManager;
 
     @Inject
     RowContentBindStage(
             NotificationRowContentBinder binder,
-            IStatusBarService statusBarService) {
+            NotifInflationErrorManager errorManager) {
         mBinder = binder;
-        mStatusBarService = statusBarService;
+        mNotifInflationErrorManager = errorManager;
     }
 
     @Override
@@ -78,24 +74,12 @@
         InflationCallback inflationCallback = new InflationCallback() {
             @Override
             public void handleInflationException(NotificationEntry entry, Exception e) {
-                entry.setHasInflationError(true);
-                try {
-                    final StatusBarNotification sbn = entry.getSbn();
-                    mStatusBarService.onNotificationError(
-                            sbn.getPackageName(),
-                            sbn.getTag(),
-                            sbn.getId(),
-                            sbn.getUid(),
-                            sbn.getInitialPid(),
-                            e.getMessage(),
-                            sbn.getUserId());
-                } catch (RemoteException ex) {
-                }
+                mNotifInflationErrorManager.setInflationError(entry, e);
             }
 
             @Override
             public void onAsyncInflationFinished(NotificationEntry entry) {
-                entry.setHasInflationError(false);
+                mNotifInflationErrorManager.clearInflationError(entry);
                 getStageParams(entry).clearDirtyContentViews();
                 callback.onStageFinished(entry);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 4b9976c..0cc3371 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -5481,7 +5481,7 @@
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void manageNotifications(View v) {
-        Intent intent = new Intent(Settings.ACTION_ALL_APPS_NOTIFICATION_SETTINGS);
+        Intent intent = new Intent(Settings.ACTION_NOTIFICATION_HISTORY);
         mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index d3e44ea..ee31300 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -492,7 +492,7 @@
                     try {
                         result = ActivityTaskManager.getService().startActivityAsUser(
                                 null, getContext().getBasePackageName(),
-                                intent,
+                                getContext().getFeatureId(), intent,
                                 intent.resolveTypeIfNeeded(getContext().getContentResolver()),
                                 null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, o.toBundle(),
                                 UserHandle.CURRENT.getIdentifier());
@@ -643,9 +643,12 @@
         if (previewBefore != null) {
             mPreviewContainer.removeView(previewBefore);
         }
+
         if (mLeftIsVoiceAssist) {
-            mLeftPreview = mPreviewInflater.inflatePreviewFromService(
-                    Dependency.get(AssistManager.class).getVoiceInteractorComponentName());
+            if (Dependency.get(AssistManager.class).getVoiceInteractorComponentName() != null) {
+                mLeftPreview = mPreviewInflater.inflatePreviewFromService(
+                        Dependency.get(AssistManager.class).getVoiceInteractorComponentName());
+            }
         } else {
             mLeftPreview = mPreviewInflater.inflatePreview(mLeftButton.getIntent());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index c68d994..4f01cc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2567,7 +2567,7 @@
             }
             try {
                 result = ActivityTaskManager.getService().startActivityAsUser(
-                        null, mContext.getBasePackageName(),
+                        null, mContext.getBasePackageName(), mContext.getFeatureId(),
                         intent,
                         intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                         null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index ef40acc..0644a42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -96,6 +96,8 @@
     private static String TAG = "StatusBarKeyguardViewManager";
 
     protected final Context mContext;
+    private final ConfigurationController mConfigurationController;
+    private final NavigationModeController mNavigationModeController;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
         @Override
@@ -209,21 +211,14 @@
         mContext = context;
         mViewMediatorCallback = callback;
         mLockPatternUtils = lockPatternUtils;
+        mConfigurationController = configurationController;
+        mNavigationModeController = navigationModeController;
         mNotificationShadeWindowController = notificationShadeWindowController;
         mKeyguardStateController = keyguardStateController;
         mMediaManager = notificationMediaManager;
         mKeyguardUpdateManager = keyguardUpdateMonitor;
-        mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback);
         mStatusBarStateController = sysuiStatusBarStateController;
-        mStatusBarStateController.addCallback(this);
-        configurationController.addCallback(this);
-        mGesturalNav = QuickStepContract.isGesturalMode(
-                navigationModeController.addListener(this));
         mDockManager = dockManager;
-        if (mDockManager != null) {
-            mDockManager.addListener(mDockEventListener);
-            mIsDocked = mDockManager.isDocked();
-        }
     }
 
     public void registerStatusBar(StatusBar statusBar,
@@ -247,6 +242,20 @@
         notificationPanelViewController.addExpansionListener(this);
         mBypassController = bypassController;
         mNotificationContainer = notificationContainer;
+
+        registerListeners();
+    }
+
+    private void registerListeners() {
+        mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback);
+        mStatusBarStateController.addCallback(this);
+        mConfigurationController.addCallback(this);
+        mGesturalNav = QuickStepContract.isGesturalMode(
+                mNavigationModeController.addListener(this));
+        if (mDockManager != null) {
+            mDockManager.addListener(mDockEventListener);
+            mIsDocked = mDockManager.isDocked();
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
index 2c7c52e..2e0c035 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java
@@ -434,6 +434,7 @@
 
         @Override
         public void handleSetListening(boolean listening) {
+            super.handleSetListening(listening);
             if (gm != null) gm.setTile(listening ? this : null);
 
             final ActivityManager am = mContext.getSystemService(ActivityManager.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
index 48a5369..fc331d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ImageWallpaperTest.java
@@ -16,35 +16,185 @@
 
 package com.android.systemui;
 
-import android.test.suitebuilder.annotation.SmallTest;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
-import androidx.test.runner.AndroidJUnit4;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManagerGlobal;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.util.Size;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.SurfaceHolder;
+
+import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
+import com.android.systemui.statusbar.phone.DozeParameters;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.util.concurrent.CountDownLatch;
 
 @SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
 public class ImageWallpaperTest extends SysuiTestCase {
+    private static final int LOW_BMP_WIDTH = 128;
+    private static final int LOW_BMP_HEIGHT = 128;
+    private static final int INVALID_BMP_WIDTH = 1;
+    private static final int INVALID_BMP_HEIGHT = 1;
+    private static final int DISPLAY_WIDTH = 1920;
+    private static final int DISPLAY_HEIGHT = 1080;
+
+    @Mock
+    private SurfaceHolder mSurfaceHolder;
+    @Mock
+    private Context mMockContext;
+    @Mock
+    private Bitmap mWallpaperBitmap;
+    @Mock
+    private DozeParameters mDozeParam;
 
     private CountDownLatch mEventCountdown;
-    private CountDownLatch mAmbientEventCountdown;
 
     @Before
     public void setUp() throws Exception {
+        com.android.systemui.util.Assert.sMainLooper = TestableLooper.get(this).getLooper();
         MockitoAnnotations.initMocks(this);
         mEventCountdown = new CountDownLatch(1);
-        mAmbientEventCountdown = new CountDownLatch(2);
+
+        WallpaperManager wallpaperManager = mock(WallpaperManager.class);
+        Resources resources = mock(Resources.class);
+
+        when(mMockContext.getSystemService(WallpaperManager.class)).thenReturn(wallpaperManager);
+        when(mMockContext.getResources()).thenReturn(resources);
+        when(resources.getConfiguration()).thenReturn(mock(Configuration.class));
+
+        DisplayInfo displayInfo = new DisplayInfo();
+        displayInfo.logicalWidth = DISPLAY_WIDTH;
+        displayInfo.logicalHeight = DISPLAY_HEIGHT;
+        when(mMockContext.getDisplay()).thenReturn(
+                new Display(mock(DisplayManagerGlobal.class), 0, displayInfo, (Resources) null));
+
+        when(wallpaperManager.getBitmap(false)).thenReturn(mWallpaperBitmap);
+        when(mWallpaperBitmap.getColorSpace()).thenReturn(ColorSpace.get(ColorSpace.Named.SRGB));
+        when(mWallpaperBitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888);
+        when(mDozeParam.getDisplayNeedsBlanking()).thenReturn(false);
+    }
+
+    private ImageWallpaper createImageWallpaper() {
+        return new ImageWallpaper(mDozeParam) {
+            @Override
+            public Engine onCreateEngine() {
+                return new GLEngine(mMockContext, mDozeParam) {
+                    @Override
+                    public Context getDisplayContext() {
+                        return mMockContext;
+                    }
+
+                    @Override
+                    public SurfaceHolder getSurfaceHolder() {
+                        return mSurfaceHolder;
+                    }
+
+                    @Override
+                    public void setFixedSizeAllowed(boolean allowed) {
+                        super.setFixedSizeAllowed(allowed);
+                        assertWithMessage("mFixedSizeAllowed should be true").that(
+                                allowed).isTrue();
+                        mEventCountdown.countDown();
+                    }
+                };
+            }
+        };
+    }
+
+    private ImageWallpaperRenderer createImageWallpaperRenderer(ImageWallpaper.GLEngine engine) {
+        return new ImageWallpaperRenderer(mMockContext, engine) {
+            @Override
+            public void startProcessingImage() {
+                loadBitmap();
+            }
+        };
     }
 
     @Test
-    public void testDeliversAmbientModeChanged() {
-        //TODO: We need add tests for GLEngine.
+    public void testBitmapWallpaper_normal() {
+        // Will use a image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH.
+        // Then we expect the surface size will be also DISPLAY_WIDTH x DISPLAY_WIDTH.
+        // Finally, we assert the transition will not be stopped.
+        verifySurfaceSizeAndAssertTransition(DISPLAY_WIDTH /* bmpWidth */,
+                DISPLAY_WIDTH /* bmpHeight */,
+                DISPLAY_WIDTH /* surfaceWidth */,
+                DISPLAY_WIDTH /* surfaceHeight */,
+                false /* assertion */);
     }
 
-    // TODO: Add more test cases for GLEngine, tracing in b/124838911.
+    @Test
+    public void testBitmapWallpaper_low_resolution() {
+        // Will use a image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT.
+        // Then we expect the surface size will be also BMP_WIDTH x BMP_HEIGHT.
+        // Finally, we assert the transition will be stopped.
+        verifySurfaceSizeAndAssertTransition(LOW_BMP_WIDTH /* bmpWidth */,
+                LOW_BMP_HEIGHT /* bmpHeight */,
+                LOW_BMP_WIDTH /* surfaceWidth */,
+                LOW_BMP_HEIGHT /* surfaceHeight */,
+                true /* assertion */);
+    }
+
+    @Test
+    public void testBitmapWallpaper_too_small() {
+        // Will use a image wallpaper with dimensions INVALID_BMP_WIDTH x INVALID_BMP_HEIGHT.
+        // Then we expect the surface size will be also MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT.
+        // Finally, we assert the transition will be stopped.
+        verifySurfaceSizeAndAssertTransition(INVALID_BMP_WIDTH /* bmpWidth */,
+                INVALID_BMP_HEIGHT /* bmpHeight */,
+                ImageWallpaper.GLEngine.MIN_SURFACE_WIDTH /* surfaceWidth */,
+                ImageWallpaper.GLEngine.MIN_SURFACE_HEIGHT /* surfaceHeight */,
+                true /* assertion */);
+    }
+
+    private void verifySurfaceSizeAndAssertTransition(int bmpWidth, int bmpHeight,
+            int surfaceWidth, int surfaceHeight, boolean assertion) {
+        ImageWallpaper.GLEngine wallpaperEngine =
+                (ImageWallpaper.GLEngine) createImageWallpaper().onCreateEngine();
+
+        ImageWallpaper.GLEngine engineSpy = spy(wallpaperEngine);
+        when(engineSpy.mIsHighEndGfx).thenReturn(true);
+
+        when(mWallpaperBitmap.getWidth()).thenReturn(bmpWidth);
+        when(mWallpaperBitmap.getHeight()).thenReturn(bmpHeight);
+
+        ImageWallpaperRenderer renderer = createImageWallpaperRenderer(engineSpy);
+        doReturn(renderer).when(engineSpy).getRendererInstance();
+        engineSpy.onCreate(engineSpy.getSurfaceHolder());
+
+        verify(mSurfaceHolder, times(1)).setFixedSize(surfaceWidth, surfaceHeight);
+        assertWithMessage("setFixedSizeAllowed should have been called.").that(
+                mEventCountdown.getCount()).isEqualTo(0);
+
+        Size frameSize = renderer.reportSurfaceSize();
+        Rect frame = new Rect(0, 0, frameSize.getWidth(), frameSize.getHeight());
+        when(mSurfaceHolder.getSurfaceFrame()).thenReturn(frame);
+
+        assertThat(engineSpy.checkIfShouldStopTransition()).isEqualTo(assertion);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
new file mode 100644
index 0000000..0aaa3b6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.classifier.brightline;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.util.DisplayMetrics;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dock.DockManager;
+import com.android.systemui.dock.DockManagerFake;
+import com.android.systemui.util.DeviceConfigProxy;
+import com.android.systemui.util.DeviceConfigProxyFake;
+import com.android.systemui.util.sensors.ProximitySensor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class BrightLineFalsingManagerTest extends SysuiTestCase {
+
+
+    @Mock
+    private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock
+    private ProximitySensor mProximitySensor;
+
+    private BrightLineFalsingManager mFalsingManager;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        DisplayMetrics dm = new DisplayMetrics();
+        dm.xdpi = 100;
+        dm.ydpi = 100;
+        dm.widthPixels = 100;
+        dm.heightPixels = 100;
+        FalsingDataProvider falsingDataProvider = new FalsingDataProvider(dm);
+        DeviceConfigProxy deviceConfigProxy = new DeviceConfigProxyFake();
+        DockManager dockManager = new DockManagerFake();
+        mFalsingManager = new BrightLineFalsingManager(falsingDataProvider,
+                mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, dockManager);
+    }
+
+    @Test
+    public void testRegisterSensor() {
+        mFalsingManager.onScreenTurningOn();
+        verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class));
+    }
+
+    @Test
+    public void testUnregisterSensor() {
+        mFalsingManager.onScreenTurningOn();
+        reset(mProximitySensor);
+        mFalsingManager.onScreenOff();
+        verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class));
+    }
+
+    @Test
+    public void testUnregisterSensor_QS() {
+        mFalsingManager.onScreenTurningOn();
+        reset(mProximitySensor);
+        mFalsingManager.setQsExpanded(true);
+        verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class));
+        mFalsingManager.setQsExpanded(false);
+        verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class));
+    }
+
+    @Test
+    public void testUnregisterSensor_Bouncer() {
+        mFalsingManager.onScreenTurningOn();
+        reset(mProximitySensor);
+        mFalsingManager.onBouncerShown();
+        verify(mProximitySensor).unregister(any(ProximitySensor.ProximitySensorListener.class));
+        mFalsingManager.onBouncerHidden();
+        verify(mProximitySensor).register(any(ProximitySensor.ProximitySensorListener.class));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
index d26ae6a..3439fe5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
@@ -119,6 +119,7 @@
         doReturn(code).when(mIActivityTaskManager).startActivityAsUser(
                 eq((IApplicationThread) null),
                 eq((String) null),
+                eq((String) null),
                 any(Intent.class),
                 eq((String) null),
                 eq((IBinder) null),
@@ -134,6 +135,7 @@
         verify(mIActivityTaskManager).startActivityAsUser(
                 eq((IApplicationThread) null),
                 eq((String) null),
+                eq((String) null),
                 any(Intent.class),
                 eq((String) null),
                 eq((IBinder) null),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index b7e9f07..d668458 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -42,6 +42,7 @@
 import com.android.systemui.SysuiBaseFragmentTest;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.CommandQueue;
@@ -104,7 +105,8 @@
                 mock(QSFactoryImpl.class), new Handler(), Looper.myLooper(),
                 mock(PluginManager.class), mock(TunerService.class),
                 () -> mock(AutoTileManager.class), mock(DumpController.class),
-                mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)));
+                mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
+                mock(QSLogger.class));
         qs.setHost(host);
 
         qs.setListening(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 38cdee4..b126d36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -35,9 +35,12 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.DumpController;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.plugins.qs.QSTileView;
 import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 
 import org.junit.Before;
@@ -65,6 +68,12 @@
     private QSCustomizer mCustomizer;
     @Mock
     private QSTileImpl dndTile;
+    @Mock
+    private BroadcastDispatcher mBroadcastDispatcher;
+    @Mock
+    private DumpController mDumpController;
+    @Mock
+    private QSLogger mQSLogger;
     private ViewGroup mParentView;
     @Mock
     private QSDetail.Callback mCallback;
@@ -78,7 +87,8 @@
         mTestableLooper = TestableLooper.get(this);
         mTestableLooper.runWithLooper(() -> {
             mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class);
-            mQsPanel = new QSPanel(mContext, null);
+            mQsPanel = new QSPanel(mContext, null, mDumpController, mBroadcastDispatcher,
+                    mQSLogger);
             // Provides a parent with non-zero size for QSPanel
             mParentView = new FrameLayout(mContext);
             mParentView.addView(mQsPanel);
@@ -97,8 +107,10 @@
     public void testSetExpanded_Metrics() {
         mQsPanel.setExpanded(true);
         verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(true));
+        verify(mQSLogger).logPanelExpanded(true, mQsPanel.getDumpableTag());
         mQsPanel.setExpanded(false);
         verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false));
+        verify(mQSLogger).logPanelExpanded(false, mQsPanel.getDumpableTag());
     }
 
     @Test
@@ -110,6 +122,14 @@
         verify(mCallback).onShowingDetail(any(), anyInt(), anyInt());
     }
 
+    @Test
+    public void setListening() {
+        when(dndTile.getTileSpec()).thenReturn("dnd");
+
+        mQsPanel.setListening(true);
+        verify(mQSLogger).logAllTilesChangeListening(true, mQsPanel.getDumpableTag(), "dnd");
+    }
+
 /*    @Test
     public void testOpenDetailsWithNullParameter_NoException() {
         mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 34111e2..d0c32ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -23,6 +23,7 @@
 
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
@@ -45,6 +46,7 @@
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.shared.plugins.PluginManager;
@@ -95,6 +97,8 @@
     @Mock
     private StatusBar mStatusBar;
     @Mock
+    private QSLogger mQSLogger;
+    @Mock
     private CustomTile mCustomTile;
 
     private Handler mHandler;
@@ -108,7 +112,7 @@
         mHandler = new Handler(mLooper.getLooper());
         mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
                 mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpController,
-                mBroadcastDispatcher, mStatusBar);
+                mBroadcastDispatcher, mStatusBar, mQSLogger);
         setUpTileFactory();
         Settings.Secure.putStringForUser(mContext.getContentResolver(), QSTileHost.TILES_SETTING,
                 "", ActivityManager.getCurrentUser());
@@ -187,6 +191,7 @@
         assertEquals(1, mQSTileHost.getTiles().size());
         QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
         assertTrue(element instanceof TestTile1);
+        verify(mQSLogger).logTileAdded("spec1");
     }
 
     @Test
@@ -200,6 +205,9 @@
         QSTile[] elements = mQSTileHost.getTiles().toArray(new QSTile[0]);
         assertTrue(elements[0] instanceof TestTile1);
         assertTrue(elements[1] instanceof TestTile2);
+
+        verify(mQSLogger).logTileAdded("spec1");
+        verify(mQSLogger).logTileAdded("spec2");
     }
 
     @Test
@@ -210,6 +218,8 @@
         mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "default");
         assertEquals(1, mQSTileHost.getTiles().size());
         assertEquals(mCustomTile, CollectionUtils.firstOrNull(mQSTileHost.getTiles()));
+
+        verify(mQSLogger).logTileAdded(CUSTOM_TILE_SPEC);
     }
 
     private static class TestQSTileHost extends QSTileHost {
@@ -217,10 +227,10 @@
                 QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper,
                 PluginManager pluginManager, TunerService tunerService,
                 Provider<AutoTileManager> autoTiles, DumpController dumpController,
-                BroadcastDispatcher broadcastDispatcher, StatusBar statusBar) {
+                BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger) {
             super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
                     tunerService, autoTiles, dumpController, broadcastDispatcher,
-                    Optional.of(statusBar));
+                    Optional.of(statusBar), qsLogger);
         }
 
         @Override
@@ -280,9 +290,6 @@
         }
 
         @Override
-        protected void handleSetListening(boolean listening) {}
-
-        @Override
         public CharSequence getTileLabel() {
             return null;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 2737b19..ab6b304 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -34,6 +34,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.phone.AutoTileManager;
@@ -78,6 +79,8 @@
     private DumpController mDumpController;
     @Mock
     private StatusBar mStatusBar;
+    @Mock
+    private QSLogger mQSLogger;
 
     @Before
     public void setUp() throws Exception {
@@ -94,7 +97,8 @@
                 () -> mAutoTileManager,
                 mDumpController,
                 mBroadcastDispatcher,
-                Optional.of(mStatusBar));
+                Optional.of(mStatusBar),
+                mQSLogger);
         mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 6bd24b9..4fada33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Matchers.argThat;
 import static org.mockito.Mockito.clearInvocations;
@@ -38,6 +39,7 @@
 
 import android.content.Intent;
 import android.metrics.LogMaker;
+import android.service.quicksettings.Tile;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
@@ -52,6 +54,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.statusbar.StatusBarState;
 
 import org.junit.Before;
@@ -61,6 +64,7 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatcher;
 import org.mockito.Captor;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
@@ -69,6 +73,11 @@
 public class QSTileImplTest extends SysuiTestCase {
 
     public static final int POSITION = 14;
+    private static final String SPEC = "spec";
+
+    @Mock
+    private QSLogger mQsLogger;
+
     private TestableLooper mTestableLooper;
     private TileImpl mTile;
     private QSTileHost mHost;
@@ -81,7 +90,6 @@
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
-        String spec = "spec";
         mTestableLooper = TestableLooper.get(this);
         mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
         mDependency.injectMockDependency(ActivityStarter.class);
@@ -89,12 +97,13 @@
         mStatusBarStateController =
             mDependency.injectMockDependency(StatusBarStateController.class);
         mHost = mock(QSTileHost.class);
-        when(mHost.indexOf(spec)).thenReturn(POSITION);
+        when(mHost.indexOf(SPEC)).thenReturn(POSITION);
         when(mHost.getContext()).thenReturn(mContext.getBaseContext());
+        when(mHost.getQSLogger()).thenReturn(mQsLogger);
 
         mTile = spy(new TileImpl(mHost));
         mTile.mHandler = mTile.new H(mTestableLooper.getLooper());
-        mTile.setTileSpec(spec);
+        mTile.setTileSpec(SPEC);
     }
 
     @Test
@@ -104,6 +113,14 @@
     }
 
     @Test
+    public void testClick_log() {
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
+
+        mTile.click();
+        verify(mQsLogger).logTileClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+    }
+
+    @Test
     public void testClick_Metrics_Status_Bar_Status() {
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
         mTile.click();
@@ -119,6 +136,14 @@
     }
 
     @Test
+    public void testSecondaryClick_log() {
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
+
+        mTile.secondaryClick();
+        verify(mQsLogger).logTileSecondaryClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+    }
+
+    @Test
     public void testSecondaryClick_Metrics_Status_Bar_Status() {
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
         mTile.secondaryClick();
@@ -133,6 +158,15 @@
         verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_LONG_PRESS)));
     }
 
+
+    @Test
+    public void testLongClick_log() {
+        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
+
+        mTile.longClick();
+        verify(mQsLogger).logTileLongClick(SPEC, StatusBarState.SHADE, Tile.STATE_ACTIVE);
+    }
+
     @Test
     public void testLongClick_Metrics_Status_Bar_Status() {
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
@@ -200,6 +234,21 @@
         verify(mTile, never()).handleStale();
     }
 
+    @Test
+    public void testHandleDestroy_log() {
+        mTile.handleDestroy();
+        verify(mQsLogger).logTileDestroyed(eq(SPEC), anyString());
+    }
+
+    @Test
+    public void testListening_log() {
+        mTile.handleSetListening(true);
+        verify(mQsLogger).logTileChangeListening(SPEC, true);
+
+        mTile.handleSetListening(false);
+        verify(mQsLogger).logTileChangeListening(SPEC, false);
+    }
+
     private class TileLogMatcher implements ArgumentMatcher<LogMaker> {
 
         private final int mCategory;
@@ -236,6 +285,7 @@
     private static class TileImpl extends QSTileImpl<QSTile.BooleanState> {
         protected TileImpl(QSHost host) {
             super(host);
+            getState().state = Tile.STATE_ACTIVE;
         }
 
         @Override
@@ -264,11 +314,6 @@
         }
 
         @Override
-        protected void handleSetListening(boolean listening) {
-
-        }
-
-        @Override
         public CharSequence getTileLabel() {
             return null;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
new file mode 100644
index 0000000..61a4fbe
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class PreparationCoordinatorTest extends SysuiTestCase {
+    private static final String TEST_MESSAGE = "TEST_MESSAGE";
+
+    private PreparationCoordinator mCoordinator;
+    private NotifFilter mInflationErrorFilter;
+    private NotifInflationErrorManager mErrorManager;
+    private NotificationEntry mEntry;
+    private Exception mInflationError;
+
+    @Mock
+    private NotifPipeline mNotifPipeline;
+    @Mock private IStatusBarService mService;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mEntry = new NotificationEntryBuilder().build();
+        mInflationError = new Exception(TEST_MESSAGE);
+        mErrorManager = new NotifInflationErrorManager();
+
+        mCoordinator = new PreparationCoordinator(
+                mock(PreparationCoordinatorLogger.class),
+                mock(NotifInflaterImpl.class),
+                mErrorManager,
+                mService);
+
+        ArgumentCaptor<NotifFilter> filterCaptor = ArgumentCaptor.forClass(NotifFilter.class);
+        mCoordinator.attach(mNotifPipeline);
+        verify(mNotifPipeline, times(2)).addPreRenderFilter(filterCaptor.capture());
+        List<NotifFilter> filters = filterCaptor.getAllValues();
+        mInflationErrorFilter = filters.get(0);
+    }
+
+    @Test
+    public void testErrorLogsToService() throws RemoteException {
+        // WHEN an entry has an inflation error.
+        mErrorManager.setInflationError(mEntry, mInflationError);
+
+        // THEN we log to status bar service.
+        verify(mService).onNotificationError(
+                eq(mEntry.getSbn().getPackageName()),
+                eq(mEntry.getSbn().getTag()),
+                eq(mEntry.getSbn().getId()),
+                eq(mEntry.getSbn().getUid()),
+                eq(mEntry.getSbn().getInitialPid()),
+                eq(mInflationError.getMessage()),
+                eq(mEntry.getSbn().getUserId()));
+    }
+
+    @Test
+    public void testFiltersOutErroredNotifications() {
+        // WHEN an entry has an inflation error.
+        mErrorManager.setInflationError(mEntry, mInflationError);
+
+        // THEN we filter it from the notification list.
+        assertTrue(mInflationErrorFilter.shouldFilterOut(mEntry, 0));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 593cbec..fd5512d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -41,7 +41,6 @@
 import android.view.LayoutInflater;
 import android.widget.RemoteViews;
 
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.TestableDependency;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.bubbles.BubblesTestActivity;
@@ -112,7 +111,7 @@
                 mock(NotifRemoteViewCache.class),
                 mock(NotificationRemoteInputManager.class));
         contentBinder.setInflateSynchronously(true);
-        mBindStage = new RowContentBindStage(contentBinder, mock(IStatusBarService.class));
+        mBindStage = new RowContentBindStage(contentBinder, mock(NotifInflationErrorManager.class));
 
         CommonNotifCollection collection = mock(CommonNotifCollection.class);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
index 775f722..d9fe655 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/RowContentBindStageTest.java
@@ -34,7 +34,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
@@ -62,7 +61,7 @@
         MockitoAnnotations.initMocks(this);
 
         mRowContentBindStage = new RowContentBindStage(mBinder,
-                mock(IStatusBarService.class));
+                mock(NotifInflationErrorManager.class));
         mRowContentBindStage.createStageParams(mEntry);
     }
 
diff --git a/packages/WAPPushManager/Android.bp b/packages/WAPPushManager/Android.bp
index cd804b2..083dac9 100644
--- a/packages/WAPPushManager/Android.bp
+++ b/packages/WAPPushManager/Android.bp
@@ -4,6 +4,7 @@
     name: "WAPPushManager",
     srcs: ["src/**/*.java"],
     platform_apis: true,
+    libs: ["telephony-common"],
     static_libs: ["android-common"],
     optimize: {
         proguard_flags_files: ["proguard.flags"],
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 48d976b..4f49fb7 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -32,14 +32,11 @@
 import android.os.RemoteException;
 import android.service.appprediction.AppPredictionService;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.infra.AbstractPerUserSystemService;
 
-import java.util.Map;
-import java.util.Set;
 import java.util.function.Consumer;
 
 /**
@@ -51,13 +48,9 @@
 
     private static final String TAG = AppPredictionPerUserService.class.getSimpleName();
 
-    /**
-     * A lookup of remote services in respect to their {@link ComponentName}.
-     */
-    @NonNull
+    @Nullable
     @GuardedBy("mLock")
-    private final ArrayMap<ComponentName, RemoteAppPredictionService> mRemoteServices =
-            new ArrayMap<>();
+    private RemoteAppPredictionService mRemoteService;
 
     /**
      * When {@code true}, remote service died but service state is kept so it's restored after
@@ -99,7 +92,7 @@
         if (enabledChanged) {
             if (!isEnabledLocked()) {
                 // Clear the remote service for the next call
-                mRemoteServices.clear();
+                mRemoteService = null;
             }
         }
         return enabledChanged;
@@ -111,14 +104,14 @@
     @GuardedBy("mLock")
     public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context,
             @NonNull AppPredictionSessionId sessionId) {
-        if (!mSessionInfos.containsKey(sessionId)) {
-            mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
-                    resolveComponentName(context), this::removeAppPredictionSessionInfo));
-        }
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.onCreatePredictionSession(context, sessionId);
+
+            if (!mSessionInfos.containsKey(sessionId)) {
+                mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
+                        this::removeAppPredictionSessionInfo));
+            }
         }
     }
 
@@ -128,8 +121,7 @@
     @GuardedBy("mLock")
     public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull AppTargetEvent event) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.notifyAppTargetEvent(sessionId, event);
         }
@@ -141,8 +133,7 @@
     @GuardedBy("mLock")
     public void notifyLaunchLocationShownLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.notifyLaunchLocationShown(sessionId, launchLocation, targetIds);
         }
@@ -154,8 +145,7 @@
     @GuardedBy("mLock")
     public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.sortAppTargets(sessionId, targets, callback);
         }
@@ -167,8 +157,7 @@
     @GuardedBy("mLock")
     public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull IPredictionCallback callback) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.registerPredictionUpdates(sessionId, callback);
 
@@ -185,8 +174,7 @@
     @GuardedBy("mLock")
     public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull IPredictionCallback callback) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.unregisterPredictionUpdates(sessionId, callback);
 
@@ -202,8 +190,7 @@
      */
     @GuardedBy("mLock")
     public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.requestPredictionUpdate(sessionId);
         }
@@ -214,8 +201,7 @@
      */
     @GuardedBy("mLock")
     public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) {
-        final RemoteAppPredictionService service = getRemoteServiceLocked(
-                getComponentName(sessionId));
+        final RemoteAppPredictionService service = getRemoteServiceLocked();
         if (service != null) {
             service.onDestroyPredictionSession(sessionId);
 
@@ -243,7 +229,7 @@
             synchronized (mLock) {
                 if (mZombie) {
                     // Sanity check - shouldn't happen
-                    if (mRemoteServices.isEmpty()) {
+                    if (mRemoteService == null) {
                         Slog.w(TAG, "Cannot resurrect sessions because remote service is null");
                         return;
                     }
@@ -280,30 +266,22 @@
     }
 
     private void destroyAndRebindRemoteService() {
-        if (mRemoteServices.isEmpty()) {
+        if (mRemoteService == null) {
             return;
         }
 
         if (isDebug()) {
             Slog.d(TAG, "Destroying the old remote service.");
         }
-        final Set<Map.Entry<ComponentName, RemoteAppPredictionService>> services =
-                new ArraySet<>(mRemoteServices.entrySet());
-        mRemoteServices.clear();
-        services.stream().forEach(entry -> destroyAndRebindRemoteService(
-                entry.getKey(), entry.getValue()));
-    }
+        mRemoteService.destroy();
+        mRemoteService = null;
 
-    private void destroyAndRebindRemoteService(
-            @NonNull final ComponentName component,
-            @NonNull final RemoteAppPredictionService service) {
-        service.destroy();
-        final RemoteAppPredictionService newService = getRemoteServiceLocked(component);
-        if (newService != null) {
+        mRemoteService = getRemoteServiceLocked();
+        if (mRemoteService != null) {
             if (isDebug()) {
                 Slog.d(TAG, "Rebinding to the new remote service.");
             }
-            newService.reconnect();
+            mRemoteService.reconnect();
         }
     }
 
@@ -314,7 +292,7 @@
     private void resurrectSessionsLocked() {
         final int numSessions = mSessionInfos.size();
         if (isDebug()) {
-            Slog.d(TAG, "Resurrecting remote service (" + mRemoteServices + ") on "
+            Slog.d(TAG, "Resurrecting remote service (" + mRemoteService + ") on "
                     + numSessions + " sessions.");
         }
 
@@ -332,49 +310,32 @@
         }
     }
 
-    @Nullable
-    private ComponentName resolveComponentName(@NonNull final AppPredictionContext context) {
-        // TODO: add logic to determine serviceName based on context
-        final String serviceName = getComponentNameLocked();
-        if (serviceName == null) {
-            if (mMaster.verbose) {
-                Slog.v(TAG, "getRemoteServiceLocked(): not set, context = " + context);
-            }
-            return null;
-        }
-        return ComponentName.unflattenFromString(serviceName);
-    }
-
-    @Nullable
-    private ComponentName getComponentName(@NonNull final AppPredictionSessionId sessionId) {
-        AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
-        return sessionInfo == null ? null : sessionInfo.mComponentName;
-    }
-
     @GuardedBy("mLock")
     @Nullable
-    private RemoteAppPredictionService getRemoteServiceLocked(
-            @Nullable final ComponentName serviceComponent) {
-        if (serviceComponent == null) return null;
-        if (!mRemoteServices.containsKey(serviceComponent)) {
-            mRemoteServices.put(serviceComponent, new RemoteAppPredictionService(getContext(),
+    private RemoteAppPredictionService getRemoteServiceLocked() {
+        if (mRemoteService == null) {
+            final String serviceName = getComponentNameLocked();
+            if (serviceName == null) {
+                if (mMaster.verbose) {
+                    Slog.v(TAG, "getRemoteServiceLocked(): not set");
+                }
+                return null;
+            }
+            ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+
+            mRemoteService = new RemoteAppPredictionService(getContext(),
                     AppPredictionService.SERVICE_INTERFACE, serviceComponent, mUserId, this,
-                    mMaster.isBindInstantServiceAllowed(), mMaster.verbose));
+                    mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
         }
 
-        return mRemoteServices.get(serviceComponent);
+        return mRemoteService;
     }
 
     private static final class AppPredictionSessionInfo {
         private static final boolean DEBUG = false;  // Do not submit with true
 
-        @NonNull
         private final AppPredictionSessionId mSessionId;
-        @NonNull
         private final AppPredictionContext mPredictionContext;
-        @Nullable
-        private final ComponentName mComponentName;
-        @NonNull
         private final Consumer<AppPredictionSessionId> mRemoveSessionInfoAction;
 
         private final RemoteCallbackList<IPredictionCallback> mCallbacks =
@@ -391,17 +352,13 @@
                     }
                 };
 
-        AppPredictionSessionInfo(
-                @NonNull final AppPredictionSessionId id,
-                @NonNull final AppPredictionContext predictionContext,
-                @Nullable final ComponentName componentName,
-                @NonNull final Consumer<AppPredictionSessionId> removeSessionInfoAction) {
+        AppPredictionSessionInfo(AppPredictionSessionId id, AppPredictionContext predictionContext,
+                Consumer<AppPredictionSessionId> removeSessionInfoAction) {
             if (DEBUG) {
                 Slog.d(TAG, "Creating AppPredictionSessionInfo for session Id=" + id);
             }
             mSessionId = id;
             mPredictionContext = predictionContext;
-            mComponentName = componentName;
             mRemoveSessionInfoAction = removeSessionInfoAction;
         }
 
@@ -433,8 +390,8 @@
         void resurrectSessionLocked(AppPredictionPerUserService service) {
             int callbackCount = mCallbacks.getRegisteredCallbackCount();
             if (DEBUG) {
-                Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked(
-                        mComponentName) + ") for session Id=" + mSessionId + " and "
+                Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked()
+                        + ") for session Id=" + mSessionId + " and "
                         + callbackCount + " callbacks.");
             }
             service.onCreatePredictionSessionLocked(mPredictionContext, mSessionId);
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index bdcd832..63d0924 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -396,6 +396,7 @@
      * @param origIntent The original intent that triggered ephemeral resolution
      * @param resolvedType The resolved type of the intent
      * @param callingPkg The app requesting the ephemeral application
+     * @param callingFeatureId The feature in the package
      * @param isRequesterInstantApp Whether or not the app requesting the ephemeral application
      *                              is an instant app
      * @param verificationBundle Optional bundle to pass to the installer for additional
@@ -404,7 +405,8 @@
      */
     public abstract void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
             Intent origIntent, String resolvedType, String callingPkg,
-            boolean isRequesterInstantApp, Bundle verificationBundle, int userId);
+            @Nullable String callingFeatureId, boolean isRequesterInstantApp,
+            Bundle verificationBundle, int userId);
 
     /**
      * Grants implicit access based on an interaction between two apps. This grants the target app
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 687fb7d..c47cde3 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -153,7 +153,6 @@
 import android.security.KeyStore;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
-import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.Log;
@@ -193,6 +192,7 @@
 import com.android.server.connectivity.NetworkDiagnostics;
 import com.android.server.connectivity.NetworkNotificationManager;
 import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
+import com.android.server.connectivity.NetworkRanker;
 import com.android.server.connectivity.PermissionMonitor;
 import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.Vpn;
@@ -229,6 +229,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.SortedSet;
+import java.util.StringJoiner;
 import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -577,6 +578,7 @@
     final ConnectivityDiagnosticsHandler mConnectivityDiagnosticsHandler;
 
     private final DnsManager mDnsManager;
+    private final NetworkRanker mNetworkRanker;
 
     private boolean mSystemReady;
     private Intent mInitialBroadcast;
@@ -725,9 +727,9 @@
         private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
                 boolean isDefaultNetwork) {
             if (DBG) {
-                log("Sending " + state +
-                        " broadcast for type " + type + " " + nai.name() +
-                        " isDefaultNetwork=" + isDefaultNetwork);
+                log("Sending " + state
+                        + " broadcast for type " + type + " " + nai.toShortString()
+                        + " isDefaultNetwork=" + isDefaultNetwork);
             }
         }
 
@@ -807,14 +809,6 @@
             }
         }
 
-        private String naiToString(NetworkAgentInfo nai) {
-            String name = nai.name();
-            String state = (nai.networkInfo != null) ?
-                    nai.networkInfo.getState() + "/" + nai.networkInfo.getDetailedState() :
-                    "???/???";
-            return name + " " + state;
-        }
-
         public void dump(IndentingPrintWriter pw) {
             pw.println("mLegacyTypeTracker:");
             pw.increaseIndent();
@@ -829,7 +823,7 @@
                 for (int type = 0; type < mTypeLists.length; type++) {
                     if (mTypeLists[type] == null || mTypeLists[type].isEmpty()) continue;
                     for (NetworkAgentInfo nai : mTypeLists[type]) {
-                        pw.println(type + " " + naiToString(nai));
+                        pw.println(type + " " + nai.toShortString());
                     }
                 }
             }
@@ -965,6 +959,7 @@
 
         mMetricsLog = logger;
         mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
+        mNetworkRanker = new NetworkRanker();
         NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder());
         mNetworkRequests.put(mDefaultRequest, defaultNRI);
         mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
@@ -2796,7 +2791,7 @@
                         nai.everCaptivePortalDetected |= visible;
                         if (nai.lastCaptivePortalDetected &&
                             Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) {
-                            if (DBG) log("Avoiding captive portal network: " + nai.name());
+                            if (DBG) log("Avoiding captive portal network: " + nai.toShortString());
                             nai.asyncChannel.sendMessage(
                                     NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
                             teardownUnneededNetwork(nai);
@@ -2855,7 +2850,7 @@
                 final String logMsg = !TextUtils.isEmpty(redirectUrl)
                         ? " with redirect to " + redirectUrl
                         : "";
-                log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
+                log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg);
             }
             if (valid != nai.lastValidated) {
                 if (wasDefault) {
@@ -3136,13 +3131,13 @@
         //    one lingered request, start lingering.
         nai.updateLingerTimer();
         if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
-            if (DBG) log("Unlingering " + nai.name());
+            if (DBG) log("Unlingering " + nai.toShortString());
             nai.unlinger();
             logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
         } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
             if (DBG) {
                 final int lingerTime = (int) (nai.getLingerExpiry() - now);
-                log("Lingering " + nai.name() + " for " + lingerTime + "ms");
+                log("Lingering " + nai.toShortString() + " for " + lingerTime + "ms");
             }
             nai.linger();
             logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
@@ -3206,7 +3201,7 @@
     private void disconnectAndDestroyNetwork(NetworkAgentInfo nai) {
         ensureRunningOnConnectivityServiceThread();
         if (DBG) {
-            log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests());
+            log(nai.toShortString() + " disconnected, was satisfying " + nai.numNetworkRequests());
         }
         // Clear all notifications of this network.
         mNotifier.clearNotification(nai.network.netId);
@@ -3264,7 +3259,7 @@
             mDefaultNetworkNai = null;
             updateDataActivityTracking(null /* newNetwork */, nai);
             notifyLockdownVpn(nai);
-            ensureNetworkTransitionWakelock(nai.name());
+            ensureNetworkTransitionWakelock(nai.toShortString());
         }
         mLegacyTypeTracker.remove(nai, wasDefault);
         if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
@@ -3487,8 +3482,8 @@
                 boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
                 nai.removeRequest(nri.request.requestId);
                 if (VDBG || DDBG) {
-                    log(" Removing from current network " + nai.name() +
-                            ", leaving " + nai.numNetworkRequests() + " requests.");
+                    log(" Removing from current network " + nai.toShortString()
+                            + ", leaving " + nai.numNetworkRequests() + " requests.");
                 }
                 // If there are still lingered requests on this network, don't tear it down,
                 // but resume lingering instead.
@@ -3497,7 +3492,7 @@
                     notifyNetworkLosing(nai, now);
                 }
                 if (unneeded(nai, UnneededFor.TEARDOWN)) {
-                    if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
+                    if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
                     teardownUnneededNetwork(nai);
                 } else {
                     wasKept = true;
@@ -3832,7 +3827,7 @@
         pw.increaseIndent();
         for (NetworkAgentInfo nai : networksSortedById()) {
             if (nai.avoidUnvalidated) {
-                pw.println(nai.name());
+                pw.println(nai.toShortString());
             }
         }
         pw.decreaseIndent();
@@ -3944,7 +3939,7 @@
 
     private void handleNetworkUnvalidated(NetworkAgentInfo nai) {
         NetworkCapabilities nc = nai.networkCapabilities;
-        if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc);
+        if (DBG) log("handleNetworkUnvalidated " + nai.toShortString() + " cap=" + nc);
 
         if (!nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
             return;
@@ -5359,7 +5354,7 @@
                 detail = reason;
             }
             log(String.format("updateSignalStrengthThresholds: %s, sending %s to %s",
-                    detail, Arrays.toString(thresholdsArray.toArray()), nai.name()));
+                    detail, Arrays.toString(thresholdsArray.toArray()), nai.toShortString()));
         }
 
         nai.asyncChannel.sendMessage(
@@ -6272,9 +6267,9 @@
         // newLp is already a defensive copy.
         newLp.ensureDirectlyConnectedRoutes();
         if (VDBG || DDBG) {
-            log("Update of LinkProperties for " + nai.name() +
-                    "; created=" + nai.created +
-                    "; everConnected=" + nai.everConnected);
+            log("Update of LinkProperties for " + nai.toShortString()
+                    + "; created=" + nai.created
+                    + "; everConnected=" + nai.everConnected);
         }
         updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
     }
@@ -6444,7 +6439,7 @@
             loge("Unknown NetworkAgentInfo in handleLingerComplete");
             return;
         }
-        if (DBG) log("handleLingerComplete for " + oldNetwork.name());
+        if (DBG) log("handleLingerComplete for " + oldNetwork.toShortString());
 
         // If we get here it means that the last linger timeout for this network expired. So there
         // must be no other active linger timers, and we must stop lingering.
@@ -6517,18 +6512,7 @@
     }
 
     // An accumulator class to gather the list of changes that result from a rematch.
-    // TODO : enrich to represent an entire set of changes to apply.
     private static class NetworkReassignment {
-        static class NetworkBgStatePair {
-            @NonNull final NetworkAgentInfo mNetwork;
-            final boolean mOldBackground;
-            NetworkBgStatePair(@NonNull final NetworkAgentInfo network,
-                    final boolean oldBackground) {
-                mNetwork = network;
-                mOldBackground = oldBackground;
-            }
-        }
-
         static class RequestReassignment {
             @NonNull public final NetworkRequestInfo mRequest;
             @Nullable public final NetworkAgentInfo mOldNetwork;
@@ -6540,44 +6524,34 @@
                 mOldNetwork = oldNetwork;
                 mNewNetwork = newNetwork;
             }
+
+            public String toString() {
+                return mRequest.request.requestId + " : "
+                        + (null != mOldNetwork ? mOldNetwork.network.netId : "null")
+                        + " → " + (null != mNewNetwork ? mNewNetwork.network.netId : "null");
+            }
         }
 
-        @NonNull private final Set<NetworkBgStatePair> mRematchedNetworks = new ArraySet<>();
-        @NonNull private final Map<NetworkRequestInfo, RequestReassignment> mReassignments =
-                new ArrayMap<>();
-
-        @NonNull Iterable<NetworkBgStatePair> getRematchedNetworks() {
-            return mRematchedNetworks;
-        }
+        @NonNull private final ArrayList<RequestReassignment> mReassignments = new ArrayList<>();
 
         @NonNull Iterable<RequestReassignment> getRequestReassignments() {
-            return mReassignments.values();
+            return mReassignments;
         }
 
         void addRequestReassignment(@NonNull final RequestReassignment reassignment) {
-            final RequestReassignment oldChange = mReassignments.get(reassignment.mRequest);
-            if (null == oldChange) {
-                mReassignments.put(reassignment.mRequest, reassignment);
-                return;
+            if (!Build.IS_USER) {
+                // The code is never supposed to add two reassignments of the same request. Make
+                // sure this stays true, but without imposing this expensive check on all
+                // reassignments on all user devices.
+                for (final RequestReassignment existing : mReassignments) {
+                    if (existing.mRequest.equals(reassignment.mRequest)) {
+                        throw new IllegalStateException("Trying to reassign ["
+                                + reassignment + "] but already have ["
+                                + existing + "]");
+                    }
+                }
             }
-            if (oldChange.mNewNetwork != reassignment.mOldNetwork) {
-                throw new IllegalArgumentException("Reassignment <" + reassignment.mRequest + "> ["
-                        + reassignment.mOldNetwork + " -> " + reassignment.mNewNetwork
-                        + "] conflicts with ["
-                        + oldChange.mOldNetwork + " -> " + oldChange.mNewNetwork + "]");
-            }
-            // There was already a note to reassign this request from a network A to a network B,
-            // and a reassignment is added from network B to some other network C. The following
-            // synthesizes the merged reassignment that goes A -> C. An interesting (but not
-            // special) case to think about is when B is null, which can happen when the rematch
-            // loop notices the current satisfier doesn't satisfy the request any more, but
-            // hasn't yet encountered another network that could.
-            mReassignments.put(reassignment.mRequest, new RequestReassignment(reassignment.mRequest,
-                    oldChange.mOldNetwork, reassignment.mNewNetwork));
-        }
-
-        void addRematchedNetwork(@NonNull final NetworkBgStatePair network) {
-            mRematchedNetworks.add(network);
+            mReassignments.add(reassignment);
         }
 
         // Will return null if this reassignment does not change the network assigned to
@@ -6589,98 +6563,25 @@
             }
             return null;
         }
-    }
 
-    // TODO : remove this when it's useless
-    @NonNull private NetworkReassignment computeInitialReassignment() {
-        final NetworkReassignment change = new NetworkReassignment();
-        for (NetworkRequestInfo nri : mNetworkRequests.values()) {
-            change.addRequestReassignment(new NetworkReassignment.RequestReassignment(nri,
-                    nri.mSatisfier, nri.mSatisfier));
-        }
-        return change;
-    }
-
-    private ArrayMap<NetworkRequestInfo, NetworkAgentInfo> computeRequestReassignmentForNetwork(
-            @NonNull final NetworkReassignment changes,
-            @NonNull final NetworkAgentInfo newNetwork) {
-        final int score = newNetwork.getCurrentScore();
-        final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests = new ArrayMap<>();
-        for (NetworkRequestInfo nri : mNetworkRequests.values()) {
-            // Process requests in the first pass and listens in the second pass. This allows us to
-            // change a network's capabilities depending on which requests it has. This is only
-            // correct if the change in capabilities doesn't affect whether the network satisfies
-            // requests or not, and doesn't affect the network's score.
-            if (nri.request.isListen()) continue;
-
-            // The reassignment has been seeded with the initial assignment, therefore
-            // getReassignment can't be null and mNewNetwork is only null if there was no
-            // satisfier in the first place or there was an explicit reassignment to null.
-            final NetworkAgentInfo currentNetwork = changes.getReassignment(nri).mNewNetwork;
-            final boolean satisfies = newNetwork.satisfies(nri.request);
-            if (newNetwork == currentNetwork && satisfies) continue;
-
-            // check if it satisfies the NetworkCapabilities
-            if (VDBG) log("  checking if request is satisfied: " + nri.request);
-            if (satisfies) {
-                // next check if it's better than any current network we're using for
-                // this request
-                if (VDBG || DDBG) {
-                    log("currentScore = "
-                            + (currentNetwork != null ? currentNetwork.getCurrentScore() : 0)
-                            + ", newScore = " + score);
-                }
-                if (currentNetwork == null || currentNetwork.getCurrentScore() < score) {
-                    reassignedRequests.put(nri, newNetwork);
-                    changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
-                            nri, currentNetwork, newNetwork));
-                }
-            } else if (newNetwork == currentNetwork) {
-                reassignedRequests.put(nri, null);
-                changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
-                        nri, currentNetwork, null));
+        public String toString() {
+            final StringJoiner sj = new StringJoiner(", " /* delimiter */,
+                    "NetReassign [" /* prefix */, "]" /* suffix */);
+            if (mReassignments.isEmpty()) return sj.add("no changes").toString();
+            for (final RequestReassignment rr : getRequestReassignments()) {
+                sj.add(rr.toString());
             }
+            return sj.toString();
         }
 
-        return reassignedRequests;
-    }
-
-    // Handles a network appearing or improving its score.
-    //
-    // - Evaluates all current NetworkRequests that can be
-    //   satisfied by newNetwork, and reassigns to newNetwork
-    //   any such requests for which newNetwork is the best.
-    //
-    // - Writes into the passed reassignment object all changes that should be done for
-    //   rematching this network with all requests, to be applied later.
-    //
-    // TODO : stop writing to the passed reassignment. This is temporarily more useful, but
-    // it's unidiomatic Java and it's hard to read.
-    //
-    // @param changes a currently-building list of changes to write to
-    // @param newNetwork is the network to be matched against NetworkRequests.
-    // @param now the time the rematch starts, as returned by SystemClock.elapsedRealtime();
-    private void rematchNetworkAndRequests(@NonNull final NetworkReassignment changes,
-            @NonNull final NetworkAgentInfo newNetwork, final long now) {
-        ensureRunningOnConnectivityServiceThread();
-        if (!newNetwork.everConnected) return;
-
-        changes.addRematchedNetwork(new NetworkReassignment.NetworkBgStatePair(newNetwork,
-                newNetwork.isBackgroundNetwork()));
-
-        if (VDBG || DDBG) log("rematching " + newNetwork.name());
-
-        final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests =
-                computeRequestReassignmentForNetwork(changes, newNetwork);
-
-        // Find and migrate to this Network any NetworkRequests for
-        // which this network is now the best.
-        for (final Map.Entry<NetworkRequestInfo, NetworkAgentInfo> entry :
-                reassignedRequests.entrySet()) {
-            final NetworkRequestInfo nri = entry.getKey();
-            final NetworkAgentInfo previousSatisfier = nri.mSatisfier;
-            final NetworkAgentInfo newSatisfier = entry.getValue();
-            updateSatisfiersForRematchRequest(nri, previousSatisfier, newSatisfier, now);
+        public String debugString() {
+            final StringBuilder sb = new StringBuilder();
+            sb.append("NetworkReassignment :");
+            if (mReassignments.isEmpty()) return sb.append(" no changes").toString();
+            for (final RequestReassignment rr : getRequestReassignments()) {
+                sb.append("\n  ").append(rr);
+            }
+            return sb.append("\n").toString();
         }
     }
 
@@ -6689,10 +6590,10 @@
             @Nullable final NetworkAgentInfo newSatisfier,
             final long now) {
         if (newSatisfier != null) {
-            if (VDBG) log("rematch for " + newSatisfier.name());
+            if (VDBG) log("rematch for " + newSatisfier.toShortString());
             if (previousSatisfier != null) {
                 if (VDBG || DDBG) {
-                    log("   accepting network in place of " + previousSatisfier.name());
+                    log("   accepting network in place of " + previousSatisfier.toShortString());
                 }
                 previousSatisfier.removeRequest(nri.request.requestId);
                 previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs);
@@ -6701,11 +6602,12 @@
             }
             newSatisfier.unlingerRequest(nri.request);
             if (!newSatisfier.addRequest(nri.request)) {
-                Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
+                Slog.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
+                        + nri.request);
             }
         } else {
             if (DBG) {
-                log("Network " + previousSatisfier.name() + " stopped satisfying"
+                log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
                         + " request " + nri.request.requestId);
             }
             previousSatisfier.removeRequest(nri.request.requestId);
@@ -6713,30 +6615,67 @@
         nri.mSatisfier = newSatisfier;
     }
 
+    @NonNull
+    private NetworkReassignment computeNetworkReassignment() {
+        ensureRunningOnConnectivityServiceThread();
+        final NetworkReassignment changes = new NetworkReassignment();
+
+        // Gather the list of all relevant agents and sort them by score.
+        final ArrayList<NetworkAgentInfo> nais = new ArrayList<>();
+        for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+            if (!nai.everConnected) continue;
+            nais.add(nai);
+        }
+
+        for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+            if (nri.request.isListen()) continue;
+            final NetworkAgentInfo bestNetwork = mNetworkRanker.getBestNetwork(nri.request, nais);
+            if (bestNetwork != nri.mSatisfier) {
+                // bestNetwork may be null if no network can satisfy this request.
+                changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
+                        nri, nri.mSatisfier, bestNetwork));
+            }
+        }
+        return changes;
+    }
+
     /**
      * Attempt to rematch all Networks with NetworkRequests.  This may result in Networks
      * being disconnected.
      */
     private void rematchAllNetworksAndRequests() {
-        // TODO: This may be slow, and should be optimized. Unfortunately at this moment the
-        // processing is network-major instead of request-major (the code iterates through all
-        // networks, then for each it iterates for all requests), which is a problem for re-scoring
-        // requests. Once the code has switched to a request-major iteration style, this can
-        // be optimized to only do the processing needed.
+        // TODO: This may be slow, and should be optimized.
         final long now = SystemClock.elapsedRealtime();
-        final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
+        final NetworkReassignment changes = computeNetworkReassignment();
+        if (VDBG || DDBG) {
+            log(changes.debugString());
+        } else if (DBG) {
+            log(changes.toString()); // Shorter form, only one line of log
+        }
+        applyNetworkReassignment(changes, now);
+    }
 
-        final NetworkAgentInfo[] nais = mNetworkAgentInfos.values().toArray(
-                new NetworkAgentInfo[mNetworkAgentInfos.size()]);
-        // Rematch higher scoring networks first to prevent requests first matching a lower
-        // scoring network and then a higher scoring network, which could produce multiple
-        // callbacks.
-        Arrays.sort(nais);
-        final NetworkReassignment changes = computeInitialReassignment();
+    private void applyNetworkReassignment(@NonNull final NetworkReassignment changes,
+            final long now) {
+        final Collection<NetworkAgentInfo> nais = mNetworkAgentInfos.values();
+
+        // Since most of the time there are only 0 or 1 background networks, it would probably
+        // be more efficient to just use an ArrayList here. TODO : measure performance
+        final ArraySet<NetworkAgentInfo> oldBgNetworks = new ArraySet<>();
         for (final NetworkAgentInfo nai : nais) {
-            rematchNetworkAndRequests(changes, nai, now);
+            if (nai.isBackgroundNetwork()) oldBgNetworks.add(nai);
         }
 
+        // First, update the lists of satisfied requests in the network agents. This is necessary
+        // because some code later depends on this state to be correct, most prominently computing
+        // the linger status.
+        for (final NetworkReassignment.RequestReassignment event :
+                changes.getRequestReassignments()) {
+            updateSatisfiersForRematchRequest(event.mRequest, event.mOldNetwork,
+                    event.mNewNetwork, now);
+        }
+
+        final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
         final NetworkRequestInfo defaultRequestInfo = mNetworkRequests.get(mDefaultRequest);
         final NetworkReassignment.RequestReassignment reassignment =
                 changes.getReassignment(defaultRequestInfo);
@@ -6761,8 +6700,6 @@
         // before LegacyTypeTracker sends legacy broadcasts
         for (final NetworkReassignment.RequestReassignment event :
                 changes.getRequestReassignments()) {
-            if (event.mOldNetwork == event.mNewNetwork) continue;
-
             // Tell NetworkProviders about the new score, so they can stop
             // trying to connect if they know they cannot match it.
             // TODO - this could get expensive if there are a lot of outstanding requests for this
@@ -6777,17 +6714,10 @@
             }
         }
 
-        for (final NetworkReassignment.NetworkBgStatePair event : changes.getRematchedNetworks()) {
-            // Process listen requests and update capabilities if the background state has
-            // changed for this network. For consistency with previous behavior, send onLost
-            // callbacks before onAvailable.
-            processNewlyLostListenRequests(event.mNetwork);
-            if (event.mOldBackground != event.mNetwork.isBackgroundNetwork()) {
-                applyBackgroundChangeForRematch(event.mNetwork);
-            }
-            processNewlySatisfiedListenRequests(event.mNetwork);
-        }
-
+        // Update the linger state before processing listen callbacks, because the background
+        // computation depends on whether the network is lingering. Don't send the LOSING callbacks
+        // just yet though, because they have to be sent after the listens are processed to keep
+        // backward compatibility.
         final ArrayList<NetworkAgentInfo> lingeredNetworks = new ArrayList<>();
         for (final NetworkAgentInfo nai : nais) {
             // Rematching may have altered the linger state of some networks, so update all linger
@@ -6800,6 +6730,19 @@
             }
         }
 
+        for (final NetworkAgentInfo nai : nais) {
+            if (!nai.everConnected) continue;
+            final boolean oldBackground = oldBgNetworks.contains(nai);
+            // Process listen requests and update capabilities if the background state has
+            // changed for this network. For consistency with previous behavior, send onLost
+            // callbacks before onAvailable.
+            processNewlyLostListenRequests(nai);
+            if (oldBackground != nai.isBackgroundNetwork()) {
+                applyBackgroundChangeForRematch(nai);
+            }
+            processNewlySatisfiedListenRequests(nai);
+        }
+
         for (final NetworkAgentInfo nai : lingeredNetworks) {
             notifyNetworkLosing(nai, now);
         }
@@ -6821,7 +6764,7 @@
                         notifyNetworkLosing(nai, now);
                     }
                 } else {
-                    if (DBG) log("Reaping " + nai.name());
+                    if (DBG) log("Reaping " + nai.toShortString());
                     teardownUnneededNetwork(nai);
                 }
             }
@@ -6849,7 +6792,7 @@
     private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
             @Nullable final NetworkAgentInfo oldDefaultNetwork,
             @Nullable final NetworkAgentInfo newDefaultNetwork,
-            @NonNull final NetworkAgentInfo[] nais) {
+            @NonNull final Collection<NetworkAgentInfo> nais) {
         if (oldDefaultNetwork != newDefaultNetwork) {
             // Maintain the illusion : since the legacy API only understands one network at a time,
             // if the default network changed, apps should see a disconnected broadcast for the
@@ -6888,7 +6831,9 @@
         // they may get old info. Reverse this after the old startUsing api is removed.
         // This is on top of the multiple intent sequencing referenced in the todo above.
         for (NetworkAgentInfo nai : nais) {
-            addNetworkToLegacyTypeTracker(nai);
+            if (nai.everConnected) {
+                addNetworkToLegacyTypeTracker(nai);
+            }
         }
     }
 
@@ -6969,8 +6914,8 @@
         notifyLockdownVpn(networkAgent);
 
         if (DBG) {
-            log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " +
-                    oldInfo.getState() + " to " + state);
+            log(networkAgent.toShortString() + " EVENT_NETWORK_INFO_CHANGED, going from "
+                    + oldInfo.getState() + " to " + state);
         }
 
         if (!networkAgent.created
@@ -6988,7 +6933,7 @@
             networkAgent.everConnected = true;
 
             if (networkAgent.linkProperties == null) {
-                Slog.wtf(TAG, networkAgent.name() + " connected with null LinkProperties");
+                Slog.wtf(TAG, networkAgent.toShortString() + " connected with null LinkProperties");
             }
 
             // NetworkCapabilities need to be set before sending the private DNS config to
@@ -7048,7 +6993,7 @@
     }
 
     private void updateNetworkScore(NetworkAgentInfo nai, NetworkScore ns) {
-        if (VDBG || DDBG) log("updateNetworkScore for " + nai.name() + " to " + ns);
+        if (VDBG || DDBG) log("updateNetworkScore for " + nai.toShortString() + " to " + ns);
         nai.setNetworkScore(ns);
         rematchAllNetworksAndRequests();
         sendUpdatedScoreToFactories(nai);
@@ -7194,14 +7139,12 @@
     protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType, int arg1) {
         if (VDBG || DDBG) {
             String notification = ConnectivityManager.getCallbackName(notifyType);
-            log("notifyType " + notification + " for " + networkAgent.name());
+            log("notifyType " + notification + " for " + networkAgent.toShortString());
         }
         for (int i = 0; i < networkAgent.numNetworkRequests(); i++) {
             NetworkRequest nr = networkAgent.requestAt(i);
             NetworkRequestInfo nri = mNetworkRequests.get(nr);
             if (VDBG) log(" sending notification for " + nr);
-            // TODO: if we're in the middle of a rematch, can we send a CAP_CHANGED callback for
-            // a network that no longer satisfies the listen?
             if (nri.mPendingIntent == null) {
                 callCallbackForRequest(nri, networkAgent, notifyType, arg1);
             } else {
@@ -7821,7 +7764,13 @@
             @NonNull NetworkAgentInfo nai, long timestampMillis, int detectionMethod,
             @NonNull PersistableBundle extras) {
         final DataStallReport report =
-                new DataStallReport(nai.network, timestampMillis, detectionMethod, extras);
+                new DataStallReport(
+                        nai.network,
+                        timestampMillis,
+                        detectionMethod,
+                        nai.linkProperties,
+                        nai.networkCapabilities,
+                        extras);
         final List<IConnectivityDiagnosticsCallback> results =
                 getMatchingPermissionedCallbacks(nai);
         for (final IConnectivityDiagnosticsCallback cb : results) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7bb9adf..c9ba988 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -224,6 +224,7 @@
      * disables FuseDaemon. If {@code 0}, uses the default value from the build system.
      */
     private static final String FUSE_ENABLED = "fuse_enabled";
+    private static final boolean DEFAULT_FUSE_ENABLED = true;
 
     public static class Lifecycle extends SystemService {
         private StorageManagerService mStorageManagerService;
@@ -1657,7 +1658,7 @@
         // If there is no value in the property yet (first boot after data wipe), this value may be
         // incorrect until #updateFusePropFromSettings where we set the correct value and reboot if
         // different
-        mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, false);
+        mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, DEFAULT_FUSE_ENABLED);
         mContext = context;
         mResolver = mContext.getContentResolver();
         mCallbacks = new Callbacks(FgThread.get().getLooper());
@@ -1720,23 +1721,17 @@
      *  and updates PROP_FUSE (reboots if changed).
      */
     private void updateFusePropFromSettings() {
-        boolean defaultFuseFlag = false;
-        boolean settingsFuseFlag = SystemProperties.getBoolean(PROP_SETTINGS_FUSE, defaultFuseFlag);
-        Slog.d(TAG, "FUSE flags. Settings: " + settingsFuseFlag + ". Default: " + defaultFuseFlag);
-
-        if (TextUtils.isEmpty(SystemProperties.get(PROP_SETTINGS_FUSE))) {
-            // Set default value of PROP_SETTINGS_FUSE and PROP_FUSE if it
-            // is unset (neither true nor false).
-            // This happens only on the first boot after wiping data partition
-            SystemProperties.set(PROP_SETTINGS_FUSE, Boolean.toString(defaultFuseFlag));
-            SystemProperties.set(PROP_FUSE, Boolean.toString(defaultFuseFlag));
-            return;
-        }
+        boolean settingsFuseFlag = SystemProperties.getBoolean(PROP_SETTINGS_FUSE,
+                DEFAULT_FUSE_ENABLED);
+        Slog.d(TAG, "FUSE flags. Settings: " + settingsFuseFlag
+                + ". Default: " + DEFAULT_FUSE_ENABLED);
 
         if (mIsFuseEnabled != settingsFuseFlag) {
             Slog.i(TAG, "Toggling persist.sys.fuse to " + settingsFuseFlag);
+            // Set prop_fuse to match prop_settings_fuse because it is used by native daemons like
+            // init, zygote, installd and vold
             SystemProperties.set(PROP_FUSE, Boolean.toString(settingsFuseFlag));
-            // Perform hard reboot to kick policy into place
+            // Then perform hard reboot to kick policy into place
             mContext.getSystemService(PowerManager.class).reboot("fuse_prop");
         }
     }
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index f2d5a9b..6763c51 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -1266,8 +1266,9 @@
             if (Sandman.shouldStartDockApp(getContext(), homeIntent)) {
                 try {
                     int result = ActivityTaskManager.getService().startActivityWithConfig(
-                            null, getContext().getBasePackageName(), homeIntent, null, null, null,
-                            0, 0, mConfiguration, null, UserHandle.USER_CURRENT);
+                            null, getContext().getBasePackageName(), getContext().getFeatureId(),
+                            homeIntent, null, null, null, 0, 0, mConfiguration, null,
+                            UserHandle.USER_CURRENT);
                     if (ActivityManager.isStartResultSuccessful(result)) {
                         dockAppStarted = true;
                     } else if (result != ActivityManager.START_INTENT_NOT_RESOLVED) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 982466d..97b5eaa 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -417,22 +417,23 @@
     }
 
     private boolean appRestrictedAnyInBackground(final int uid, final String packageName) {
-        final int mode = mAm.mAppOpsService.checkOperation(
+        final int mode = mAm.getAppOpsManager().checkOpNoThrow(
                 AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName);
         return (mode != AppOpsManager.MODE_ALLOWED);
     }
 
     ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
-            int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
+            int callingPid, int callingUid, boolean fgRequired, String callingPackage,
+            @Nullable String callingFeatureId, final int userId)
             throws TransactionTooLargeException {
         return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
-                callingPackage, userId, false);
+                callingPackage, callingFeatureId, userId, false);
     }
 
     ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
             int callingPid, int callingUid, boolean fgRequired, String callingPackage,
-            final int userId, boolean allowBackgroundActivityStarts)
-            throws TransactionTooLargeException {
+            @Nullable String callingFeatureId, final int userId,
+            boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
         if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
                 + " type=" + resolvedType + " args=" + service.getExtras());
 
@@ -488,7 +489,7 @@
         // If this is a direct-to-foreground start, make sure it is allowed as per the app op.
         boolean forceSilentAbort = false;
         if (fgRequired) {
-            final int mode = mAm.mAppOpsService.checkOperation(
+            final int mode = mAm.getAppOpsManager().checkOpNoThrow(
                     AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
             switch (mode) {
                 case AppOpsManager.MODE_ALLOWED:
@@ -566,7 +567,7 @@
         // review is completed.
 
         // XXX This is not dealing with fgRequired!
-        if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
+        if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage, callingFeatureId,
                 callingUid, service, callerFg, userId)) {
             return null;
         }
@@ -673,8 +674,8 @@
     }
 
     private boolean requestStartTargetPermissionsReviewIfNeededLocked(ServiceRecord r,
-            String callingPackage, int callingUid, Intent service, boolean callerFg,
-            final int userId) {
+            String callingPackage, @Nullable String callingFeatureId, int callingUid,
+            Intent service, boolean callerFg, final int userId) {
         if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                 r.packageName, r.userId)) {
 
@@ -686,7 +687,7 @@
             }
 
             IIntentSender target = mAm.mPendingIntentController.getIntentSender(
-                    ActivityManager.INTENT_SENDER_SERVICE, callingPackage,
+                    ActivityManager.INTENT_SENDER_SERVICE, callingPackage, callingFeatureId,
                     callingUid, userId, null, null, 0, new Intent[]{service},
                     new String[]{service.resolveType(mAm.mContext.getContentResolver())},
                     PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
@@ -1287,7 +1288,7 @@
             }
             // Instant apps need permission to create foreground services.
             if (r.appInfo.isInstantApp()) {
-                final int mode = mAm.mAppOpsService.checkOperation(
+                final int mode = mAm.getAppOpsManager().checkOpNoThrow(
                         AppOpsManager.OP_INSTANT_APP_START_FOREGROUND,
                         r.appInfo.uid,
                         r.appInfo.packageName);
@@ -1354,7 +1355,7 @@
 
             try {
                 boolean ignoreForeground = false;
-                final int mode = mAm.mAppOpsService.checkOperation(
+                final int mode = mAm.getAppOpsManager().checkOpNoThrow(
                         AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
                 switch (mode) {
                     case AppOpsManager.MODE_ALLOWED:
@@ -2289,7 +2290,7 @@
                 return new ServiceLookupResult(null, r.permission);
             } else if (r.permission != null && callingPackage != null) {
                 final int opCode = AppOpsManager.permissionToOpCode(r.permission);
-                if (opCode != AppOpsManager.OP_NONE && mAm.mAppOpsService.checkOperation(
+                if (opCode != AppOpsManager.OP_NONE && mAm.getAppOpsManager().checkOpNoThrow(
                         opCode, callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: Accessing service " + r.shortInstanceName
                             + " from pid=" + callingPid
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c01766f..0852458 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1240,6 +1240,7 @@
      * Information about and control over application operations
      */
     final AppOpsService mAppOpsService;
+    private AppOpsManager mAppOpsManager;
 
     /**
      * List of initialization arguments to pass to all processes when binding applications to them.
@@ -2109,7 +2110,7 @@
                 new IAppOpsCallback.Stub() {
                     @Override public void opChanged(int op, int uid, String packageName) {
                         if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && packageName != null) {
-                            if (mAppOpsService.checkOperation(op, uid, packageName)
+                            if (getAppOpsManager().checkOpNoThrow(op, uid, packageName)
                                     != AppOpsManager.MODE_ALLOWED) {
                                 runInBackgroundDisabled(uid);
                             }
@@ -2400,6 +2401,13 @@
         }
     }
 
+    AppOpsManager getAppOpsManager() {
+        if (mAppOpsManager == null) {
+            mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+        }
+        return mAppOpsManager;
+    }
+
     /**
      * Provides the basic functionality for activity task related tests when a handler thread is
      * given to initialize the dependency members.
@@ -2908,7 +2916,7 @@
     @Override
     public void batterySendBroadcast(Intent intent) {
         synchronized (this) {
-            broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+            broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
                     OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(),
                     Binder.getCallingPid(), UserHandle.USER_ALL);
         }
@@ -3512,30 +3520,56 @@
 
     }
 
+    /**
+     * @deprecated use {@link #startActivityWithFeature} instead
+     */
+    @Deprecated
     @Override
     public int startActivity(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
-        return mActivityTaskManager.startActivity(caller, callingPackage, intent, resolvedType,
-                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);
+        return mActivityTaskManager.startActivity(caller, callingPackage, null, intent,
+                resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);
     }
 
     @Override
+    public int startActivityWithFeature(IApplicationThread caller, String callingPackage,
+            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
+            Bundle bOptions) {
+        return mActivityTaskManager.startActivity(caller, callingPackage, callingFeatureId, intent,
+                resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);
+    }
+
+    /**
+     * @deprecated use {@link #startActivityAsUserWithFeature} instead
+     */
+    @Deprecated
+    @Override
     public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
             Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
             int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+        return startActivityAsUserWithFeature(caller, callingPackage, null, intent, resolvedType,
+                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, userId);
+    }
 
-            return mActivityTaskManager.startActivityAsUser(caller, callingPackage, intent,
-                    resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo,
-                    bOptions, userId);
+    @Override
+    public final int startActivityAsUserWithFeature(IApplicationThread caller,
+            String callingPackage, String callingFeatureId, Intent intent, String resolvedType,
+            IBinder resultTo, String resultWho, int requestCode, int startFlags,
+            ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+        return mActivityTaskManager.startActivityAsUser(caller, callingPackage,
+                    callingFeatureId, intent, resolvedType, resultTo, resultWho, requestCode,
+                    startFlags, profilerInfo, bOptions, userId);
     }
 
     WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
-            return mActivityTaskManager.startActivityAndWait(caller, callingPackage, intent,
-                    resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo,
-                    bOptions, userId);
+            @Nullable String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
+            Bundle bOptions, int userId) {
+            return mActivityTaskManager.startActivityAndWait(caller, callingPackage,
+                    callingFeatureId, intent, resolvedType, resultTo, resultWho, requestCode,
+                    startFlags, profilerInfo, bOptions, userId);
     }
 
     @Override
@@ -4106,12 +4140,12 @@
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
                     if (isInstantApp) {
                         intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
-                        broadcastIntentInPackage("android", SYSTEM_UID, uid, pid, intent, null,
-                                null, 0, null, null, permission.ACCESS_INSTANT_APPS, null, false,
-                                false, resolvedUserId, false);
+                        broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
+                                null, null, 0, null, null, permission.ACCESS_INSTANT_APPS, null,
+                                false, false, resolvedUserId, false);
                     } else {
-                        broadcastIntentInPackage("android", SYSTEM_UID, uid, pid, intent, null,
-                                null, 0, null, null, null, null, false, false, resolvedUserId,
+                        broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
+                                null, null, 0, null, null, null, null, false, false, resolvedUserId,
                                 false);
                     }
 
@@ -4559,7 +4593,7 @@
         }
         intent.putExtra(Intent.EXTRA_UID, uid);
         intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid));
-        broadcastIntentLocked(null, null, intent,
+        broadcastIntentLocked(null, null, null, intent,
                 null, null, 0, null, null, null, OP_NONE,
                 null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
                 Binder.getCallingPid(), UserHandle.getUserId(uid));
@@ -5416,12 +5450,23 @@
         }
     }
 
+    /**
+     * @deprecated Use {@link #getIntentSenderWithFeature} instead
+     */
+    @Deprecated
     @Override
     public IIntentSender getIntentSender(int type,
             String packageName, IBinder token, String resultWho,
             int requestCode, Intent[] intents, String[] resolvedTypes,
             int flags, Bundle bOptions, int userId) {
+        return getIntentSenderWithFeature(type, packageName, null, token, resultWho, requestCode,
+                intents, resolvedTypes, flags, bOptions, userId);
+    }
 
+    @Override
+    public IIntentSender getIntentSenderWithFeature(int type, String packageName, String featureId,
+            IBinder token, String resultWho, int requestCode, Intent[] intents,
+            String[] resolvedTypes, int flags, Bundle bOptions, int userId) {
         // NOTE: The service lock isn't held in this method because nothing in the method requires
         // the service lock to be held.
 
@@ -5483,12 +5528,13 @@
             }
 
             if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
-                return mAtmInternal.getIntentSender(type, packageName, callingUid, userId,
-                        token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
+                return mAtmInternal.getIntentSender(type, packageName, featureId, callingUid,
+                        userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
+                        bOptions);
             }
-            return mPendingIntentController.getIntentSender(type, packageName, callingUid,
-                    userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
-                    bOptions);
+            return mPendingIntentController.getIntentSender(type, packageName, featureId,
+                    callingUid, userId, token, resultWho, requestCode, intents, resolvedTypes,
+                    flags, bOptions);
         } catch (RemoteException e) {
             throw new SecurityException(e);
         }
@@ -6054,8 +6100,8 @@
             return ActivityManager.APP_START_MODE_DELAYED;
         }
         // Not in the RESTRICTED bucket so policy is based on AppOp check.
-        int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
-                uid, packageName, null, false, "");
+        int appop = getAppOpsManager().noteOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND,
+                uid, packageName, null, "");
         if (DEBUG_BACKGROUND_CHECK) {
             Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop);
         }
@@ -7961,7 +8007,7 @@
     }
 
     boolean isBackgroundRestrictedNoCheck(final int uid, final String packageName) {
-        final int mode = mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+        final int mode = getAppOpsManager().checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
                 uid, packageName);
         return mode != AppOpsManager.MODE_ALLOWED;
     }
@@ -9439,14 +9485,14 @@
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                             | Intent.FLAG_RECEIVER_FOREGROUND);
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
-                    broadcastIntentLocked(null, null, intent,
+                    broadcastIntentLocked(null, null, null, intent,
                             null, null, 0, null, null, null, OP_NONE,
                             null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
                             currentUserId);
                     intent = new Intent(Intent.ACTION_USER_STARTING);
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
-                    broadcastIntentLocked(null, null, intent, null,
+                    broadcastIntentLocked(null, null, null, intent, null,
                             new IIntentReceiver.Stub() {
                                 @Override
                                 public void performReceive(Intent intent, int resultCode,
@@ -14613,7 +14659,8 @@
 
     @Override
     public ComponentName startService(IApplicationThread caller, Intent service,
-            String resolvedType, boolean requireForeground, String callingPackage, int userId)
+            String resolvedType, boolean requireForeground, String callingPackage,
+            String callingFeatureId, int userId)
             throws TransactionTooLargeException {
         enforceNotIsolatedCaller("startService");
         // Refuse possible leaked file descriptors
@@ -14635,7 +14682,7 @@
             try {
                 res = mServices.startServiceLocked(caller, service,
                         resolvedType, callingPid, callingUid,
-                        requireForeground, callingPackage, userId);
+                        requireForeground, callingPackage, callingFeatureId, userId);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -15113,9 +15160,20 @@
         return didSomething;
     }
 
+    /**
+     * @deprecated Use {@link #registerReceiverWithFeature}
+     */
+    @Deprecated
     public Intent registerReceiver(IApplicationThread caller, String callerPackage,
             IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
             int flags) {
+        return registerReceiverWithFeature(caller, callerPackage, null, receiver, filter,
+                permission, userId, flags);
+    }
+
+    public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
+            String callerFeatureId, IIntentReceiver receiver, IntentFilter filter,
+            String permission, int userId, int flags) {
         enforceNotIsolatedCaller("registerReceiver");
         ArrayList<Intent> stickyIntents = null;
         ProcessRecord callerApp = null;
@@ -15251,7 +15309,7 @@
                         + " was previously registered for user " + rl.userId
                         + " callerPackage is " + callerPackage);
             }
-            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
+            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
                     permission, callingUid, userId, instantApp, visibleToInstantApps);
             if (rl.containsFilter(filter)) {
                 Slog.w(TAG, "Receiver with filter " + filter
@@ -15276,7 +15334,7 @@
                     Intent intent = allSticky.get(i);
                     BroadcastQueue queue = broadcastQueueForIntent(intent);
                     BroadcastRecord r = new BroadcastRecord(queue, intent, null,
-                            null, -1, -1, false, null, null, OP_NONE, null, receivers,
+                            null, null, -1, -1, false, null, null, OP_NONE, null, receivers,
                             null, 0, null, null, false, true, true, -1, false,
                             false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
                     queue.enqueueParallelBroadcastLocked(r);
@@ -15506,20 +15564,20 @@
 
     @GuardedBy("this")
     final int broadcastIntentLocked(ProcessRecord callerApp,
-            String callerPackage, Intent intent, String resolvedType,
+            String callerPackage, String callerFeatureId, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData,
             Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
             boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
             int realCallingPid, int userId) {
-        return broadcastIntentLocked(callerApp, callerPackage, intent, resolvedType, resultTo,
-            resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions, ordered,
-            sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId,
-            false /* allowBackgroundActivityStarts */);
+        return broadcastIntentLocked(callerApp, callerPackage, callerFeatureId, intent,
+                resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions,
+                appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
+                realCallingPid, userId, false /* allowBackgroundActivityStarts */);
     }
 
     @GuardedBy("this")
-    final int broadcastIntentLocked(ProcessRecord callerApp,
-            String callerPackage, Intent intent, String resolvedType,
+    final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
+            @Nullable String callerFeatureId, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData,
             Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
             boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
@@ -16056,8 +16114,8 @@
                         isProtectedBroadcast, registeredReceivers);
             }
             final BroadcastQueue queue = broadcastQueueForIntent(intent);
-            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
-                    callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
+            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
+                    callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
                     resultCode, resultData, resultExtras, ordered, sticky, false, userId,
                     allowBackgroundActivityStarts, timeoutExempt);
@@ -16153,8 +16211,8 @@
         if ((receivers != null && receivers.size() > 0)
                 || resultTo != null) {
             BroadcastQueue queue = broadcastQueueForIntent(intent);
-            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
-                    callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
+            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
+                    callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                     resultData, resultExtras, ordered, sticky, false, userId,
                     allowBackgroundActivityStarts, timeoutExempt);
@@ -16273,11 +16331,25 @@
         return intent;
     }
 
+    /**
+     * @deprecated Use {@link #broadcastIntentWithFeature}
+     */
+    @Deprecated
     public final int broadcastIntent(IApplicationThread caller,
             Intent intent, String resolvedType, IIntentReceiver resultTo,
             int resultCode, String resultData, Bundle resultExtras,
             String[] requiredPermissions, int appOp, Bundle bOptions,
             boolean serialized, boolean sticky, int userId) {
+        return broadcastIntentWithFeature(caller, null, intent, resolvedType, resultTo, resultCode,
+                resultData, resultExtras, requiredPermissions, appOp, bOptions, serialized, sticky,
+                userId);
+    }
+
+    public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
+            Intent intent, String resolvedType, IIntentReceiver resultTo,
+            int resultCode, String resultData, Bundle resultExtras,
+            String[] requiredPermissions, int appOp, Bundle bOptions,
+            boolean serialized, boolean sticky, int userId) {
         enforceNotIsolatedCaller("broadcastIntent");
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
@@ -16289,7 +16361,7 @@
             final long origId = Binder.clearCallingIdentity();
             try {
                 return broadcastIntentLocked(callerApp,
-                        callerApp != null ? callerApp.info.packageName : null,
+                        callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
                         intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                         requiredPermissions, appOp, bOptions, serialized, sticky,
                         callingPid, callingUid, callingUid, callingPid, userId);
@@ -16299,9 +16371,9 @@
         }
     }
 
-    int broadcastIntentInPackage(String packageName, int uid, int realCallingUid,
-            int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo,
-            int resultCode, String resultData, Bundle resultExtras,
+    int broadcastIntentInPackage(String packageName, @Nullable String featureId, int uid,
+            int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
+            IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
             String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
             int userId, boolean allowBackgroundActivityStarts) {
         synchronized(this) {
@@ -16311,11 +16383,10 @@
             String[] requiredPermissions = requiredPermission == null ? null
                     : new String[] {requiredPermission};
             try {
-                return broadcastIntentLocked(null, packageName, intent, resolvedType,
-                        resultTo, resultCode, resultData, resultExtras,
-                        requiredPermissions, OP_NONE, bOptions, serialized,
-                        sticky, -1, uid, realCallingUid, realCallingPid, userId,
-                        allowBackgroundActivityStarts);
+                return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
+                        resultTo, resultCode, resultData, resultExtras, requiredPermissions,
+                        OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid,
+                        realCallingPid, userId, allowBackgroundActivityStarts);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -18919,23 +18990,24 @@
         }
 
         @Override
-        public int broadcastIntentInPackage(String packageName, int uid, int realCallingUid,
-                int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo,
-                int resultCode, String resultData, Bundle resultExtras, String requiredPermission,
-                Bundle bOptions, boolean serialized, boolean sticky, int userId,
-                boolean allowBackgroundActivityStarts) {
+        public int broadcastIntentInPackage(String packageName, @Nullable String featureId, int uid,
+                int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
+                IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
+                String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
+                int userId, boolean allowBackgroundActivityStarts) {
             synchronized (ActivityManagerService.this) {
-                return ActivityManagerService.this.broadcastIntentInPackage(packageName, uid,
-                        realCallingUid, realCallingPid, intent, resolvedType, resultTo, resultCode,
-                        resultData, resultExtras, requiredPermission, bOptions, serialized, sticky,
-                        userId, allowBackgroundActivityStarts);
+                return ActivityManagerService.this.broadcastIntentInPackage(packageName, featureId,
+                        uid, realCallingUid, realCallingPid, intent, resolvedType, resultTo,
+                        resultCode, resultData, resultExtras, requiredPermission, bOptions,
+                        serialized, sticky, userId, allowBackgroundActivityStarts);
             }
         }
 
         @Override
         public ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
-                boolean fgRequired, String callingPackage, int userId,
-                boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
+                boolean fgRequired, String callingPackage, @Nullable String callingFeatureId,
+                int userId, boolean allowBackgroundActivityStarts)
+                throws TransactionTooLargeException {
             synchronized(ActivityManagerService.this) {
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                         "startServiceInPackage: " + service + " type=" + resolvedType);
@@ -18943,8 +19015,8 @@
                 ComponentName res;
                 try {
                     res = mServices.startServiceLocked(null, service,
-                            resolvedType, -1, uid, fgRequired, callingPackage, userId,
-                            allowBackgroundActivityStarts);
+                            resolvedType, -1, uid, fgRequired, callingPackage,
+                            callingFeatureId, userId, allowBackgroundActivityStarts);
                 } finally {
                     Binder.restoreCallingIdentity(origId);
                 }
@@ -19037,7 +19109,7 @@
                         | Intent.FLAG_RECEIVER_REPLACE_PENDING
                         | Intent.FLAG_RECEIVER_FOREGROUND
                         | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
-                broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
                         OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
                         Binder.getCallingPid(), UserHandle.USER_ALL);
                 if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
@@ -19048,7 +19120,7 @@
                     if (initLocale || !mProcessesReady) {
                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                     }
-                    broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                    broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
                             OP_NONE, null, false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(),
                             Binder.getCallingPid(), UserHandle.USER_ALL);
                 }
@@ -19063,7 +19135,7 @@
                     // Typically only app stores will have this permission.
                     String[] permissions =
                             new String[] { android.Manifest.permission.INSTALL_PACKAGES };
-                    broadcastIntentLocked(null, null, intent, null, null, 0, null, null,
+                    broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null,
                             permissions, OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
                             Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL);
                 }
@@ -19088,7 +19160,7 @@
                     intent.putExtra("reason", reason);
                 }
 
-                broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+                broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
                         OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(),
                         Binder.getCallingPid(), UserHandle.USER_ALL);
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 73ca31e..c7f5f63 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -536,13 +536,13 @@
                 options.setLockTaskEnabled(true);
             }
             if (mWaitOption) {
-                result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, intent, mimeType,
-                        null, null, 0, mStartFlags, profilerInfo,
+                result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, null, intent,
+                        mimeType, null, null, 0, mStartFlags, profilerInfo,
                         options != null ? options.toBundle() : null, mUserId);
                 res = result.result;
             } else {
-                res = mInternal.startActivityAsUser(null, SHELL_PACKAGE_NAME, intent, mimeType,
-                        null, null, 0, mStartFlags, profilerInfo,
+                res = mInternal.startActivityAsUserWithFeature(null, SHELL_PACKAGE_NAME, null,
+                        intent, mimeType, null, null, 0, mStartFlags, profilerInfo,
                         options != null ? options.toBundle() : null, mUserId);
             }
             final long endTime = SystemClock.uptimeMillis();
@@ -654,7 +654,7 @@
         pw.println("Starting service: " + intent);
         pw.flush();
         ComponentName cn = mInterface.startService(null, intent, intent.getType(),
-                asForeground, SHELL_PACKAGE_NAME, mUserId);
+                asForeground, SHELL_PACKAGE_NAME, null, mUserId);
         if (cn == null) {
             err.println("Error: Not found; no service started.");
             return -1;
@@ -744,8 +744,9 @@
         pw.println("Broadcasting: " + intent);
         pw.flush();
         Bundle bundle = mBroadcastOptions == null ? null : mBroadcastOptions.toBundle();
-        mInterface.broadcastIntent(null, intent, null, receiver, 0, null, null, requiredPermissions,
-                android.app.AppOpsManager.OP_NONE, bundle, true, false, mUserId);
+        mInterface.broadcastIntentWithFeature(null, null, intent, null, receiver, 0, null, null,
+                requiredPermissions, android.app.AppOpsManager.OP_NONE, bundle, true, false,
+                mUserId);
         receiver.waitForFinish();
         return 0;
     }
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index 1ec8db0..578db6f 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -27,6 +27,7 @@
     // Back-pointer to the list this filter is in.
     final ReceiverList receiverList;
     final String packageName;
+    final String featureId;
     final String requiredPermission;
     final int owningUid;
     final int owningUserId;
@@ -34,11 +35,12 @@
     final boolean visibleToInstantApp;
 
     BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
-            String _packageName, String _requiredPermission, int _owningUid, int _userId,
+            String _packageName, String _featureId, String _requiredPermission, int _owningUid, int _userId,
             boolean _instantApp, boolean _visibleToInstantApp) {
         super(_filter);
         receiverList = _receiverList;
         packageName = _packageName;
+        featureId = _featureId;
         requiredPermission = _requiredPermission;
         owningUid = _owningUid;
         owningUserId = _userId;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 6697b5a..26ef707 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -648,10 +648,10 @@
                 skip = true;
             } else {
                 final int opCode = AppOpsManager.permissionToOpCode(filter.requiredPermission);
-                // TODO moltmann: Set featureId from caller
                 if (opCode != AppOpsManager.OP_NONE
-                        && mService.mAppOpsService.noteOperation(opCode, r.callingUid,
-                        r.callerPackage, null, false, "") != AppOpsManager.MODE_ALLOWED) {
+                        && mService.getAppOpsManager().noteOpNoThrow(opCode, r.callingUid,
+                        r.callerPackage, r.callerFeatureId, "")
+                        != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: broadcasting "
                             + r.intent.toString()
                             + " from " + r.callerPackage + " (pid="
@@ -681,10 +681,9 @@
                     break;
                 }
                 int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
-                // TODO moltmann: Set featureId from caller
                 if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
-                        && mService.mAppOpsService.noteOperation(appOp,
-                        filter.receiverList.uid, filter.packageName, null, false, "")
+                        && mService.getAppOpsManager().noteOpNoThrow(appOp,
+                        filter.receiverList.uid, filter.packageName, filter.featureId, "")
                         != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: receiving "
                             + r.intent.toString()
@@ -714,10 +713,9 @@
                 skip = true;
             }
         }
-        // TODO moltmann: Set featureId from caller
         if (!skip && r.appOp != AppOpsManager.OP_NONE
-                && mService.mAppOpsService.noteOperation(r.appOp,
-                filter.receiverList.uid, filter.packageName, null, false, "")
+                && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
+                filter.receiverList.uid, filter.packageName, filter.featureId, "")
                 != AppOpsManager.MODE_ALLOWED) {
             Slog.w(TAG, "Appop Denial: receiving "
                     + r.intent.toString()
@@ -863,7 +861,8 @@
         if (callerForeground && receiverRecord.intent.getComponent() != null) {
             IIntentSender target = mService.mPendingIntentController.getIntentSender(
                     ActivityManager.INTENT_SENDER_BROADCAST, receiverRecord.callerPackage,
-                    receiverRecord.callingUid, receiverRecord.userId, null, null, 0,
+                    receiverRecord.callerFeatureId, receiverRecord.callingUid,
+                    receiverRecord.userId, null, null, 0,
                     new Intent[]{receiverRecord.intent},
                     new String[]{receiverRecord.intent.resolveType(mService.mContext
                             .getContentResolver())},
@@ -1371,10 +1370,9 @@
             skip = true;
         } else if (!skip && info.activityInfo.permission != null) {
             final int opCode = AppOpsManager.permissionToOpCode(info.activityInfo.permission);
-            // TODO moltmann: Set featureId from caller
             if (opCode != AppOpsManager.OP_NONE
-                    && mService.mAppOpsService.noteOperation(opCode, r.callingUid, r.callerPackage,
-                    null, false, "") != AppOpsManager.MODE_ALLOWED) {
+                    && mService.getAppOpsManager().noteOpNoThrow(opCode, r.callingUid, r.callerPackage,
+                    r.callerFeatureId, "") != AppOpsManager.MODE_ALLOWED) {
                 Slog.w(TAG, "Appop Denial: broadcasting "
                         + r.intent.toString()
                         + " from " + r.callerPackage + " (pid="
@@ -1410,11 +1408,10 @@
                     break;
                 }
                 int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
-                // TODO moltmann: Set featureId from caller
                 if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
-                        && mService.mAppOpsService.noteOperation(appOp,
-                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, null,
-                        false, "")
+                        && mService.getAppOpsManager().noteOpNoThrow(appOp,
+                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
+                        null /* default featureId */, "")
                         != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: receiving "
                             + r.intent + " to "
@@ -1428,11 +1425,10 @@
                 }
             }
         }
-        // TODO moltmann: Set featureId from caller
         if (!skip && r.appOp != AppOpsManager.OP_NONE
-                && mService.mAppOpsService.noteOperation(r.appOp,
-                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, null, false,
-                "")
+                && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
+                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
+                null  /* default featureId */, "")
                 != AppOpsManager.MODE_ALLOWED) {
             Slog.w(TAG, "Appop Denial: receiving "
                     + r.intent + " to "
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index f263886..8ef67f9 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.content.ComponentName;
@@ -51,6 +52,7 @@
     final ComponentName targetComp; // original component name set on the intent
     final ProcessRecord callerApp; // process that sent this
     final String callerPackage; // who sent this
+    final @Nullable String callerFeatureId; // which feature in the package sent this
     final int callingPid;   // the pid of who sent this
     final int callingUid;   // the uid of who sent this
     final boolean callerInstantApp; // caller is an Instant App?
@@ -233,7 +235,8 @@
 
     BroadcastRecord(BroadcastQueue _queue,
             Intent _intent, ProcessRecord _callerApp, String _callerPackage,
-            int _callingPid, int _callingUid, boolean _callerInstantApp, String _resolvedType,
+            @Nullable String _callerFeatureId, int _callingPid, int _callingUid,
+            boolean _callerInstantApp, String _resolvedType,
             String[] _requiredPermissions, int _appOp, BroadcastOptions _options, List _receivers,
             IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras,
             boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId,
@@ -246,6 +249,7 @@
         targetComp = _intent.getComponent();
         callerApp = _callerApp;
         callerPackage = _callerPackage;
+        callerFeatureId = _callerFeatureId;
         callingPid = _callingPid;
         callingUid = _callingUid;
         callerInstantApp = _callerInstantApp;
@@ -280,6 +284,7 @@
 
         callerApp = from.callerApp;
         callerPackage = from.callerPackage;
+        callerFeatureId = from.callerFeatureId;
         callingPid = from.callingPid;
         callingUid = from.callingUid;
         callerInstantApp = from.callerInstantApp;
@@ -343,8 +348,8 @@
         }
 
         // build a new BroadcastRecord around that single-target list
-        BroadcastRecord split = new BroadcastRecord(queue, intent, callerApp,
-                callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
+        BroadcastRecord split = new BroadcastRecord(queue, intent, callerApp, callerPackage,
+                callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                 requiredPermissions, appOp, options, splitReceivers, resultTo, resultCode,
                 resultData, resultExtras, ordered, sticky, initialSticky, userId,
                 allowBackgroundActivityStarts, timeoutExempt);
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index df76713..eacf088 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -23,6 +23,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
@@ -88,9 +89,9 @@
         }
     }
 
-    public PendingIntentRecord getIntentSender(int type, String packageName, int callingUid,
-            int userId, IBinder token, String resultWho, int requestCode, Intent[] intents,
-            String[] resolvedTypes, int flags, Bundle bOptions) {
+    public PendingIntentRecord getIntentSender(int type, String packageName,
+            @Nullable String featureId, int callingUid, int userId, IBinder token, String resultWho,
+            int requestCode, Intent[] intents, String[] resolvedTypes, int flags, Bundle bOptions) {
         synchronized (mLock) {
             if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSender(): uid=" + callingUid);
 
@@ -109,8 +110,8 @@
             flags &= ~(PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_CANCEL_CURRENT
                     | PendingIntent.FLAG_UPDATE_CURRENT);
 
-            PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, token,
-                    resultWho, requestCode, intents, resolvedTypes, flags,
+            PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, featureId,
+                    token, resultWho, requestCode, intents, resolvedTypes, flags,
                     SafeActivityOptions.fromBundle(bOptions), userId);
             WeakReference<PendingIntentRecord> ref;
             ref = mIntentSenderRecords.get(key);
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 3ba2210..d54d2d7 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -17,14 +17,16 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.START_SUCCESS;
+
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.content.IIntentSender;
-import android.content.IIntentReceiver;
 import android.app.PendingIntent;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
 import android.content.Intent;
 import android.os.Binder;
 import android.os.Bundle;
@@ -72,6 +74,7 @@
     final static class Key {
         final int type;
         final String packageName;
+        final String featureId;
         final IBinder activity;
         final String who;
         final int requestCode;
@@ -86,10 +89,11 @@
 
         private static final int ODD_PRIME_NUMBER = 37;
 
-        Key(int _t, String _p, IBinder _a, String _w,
+        Key(int _t, String _p, @Nullable String _featureId, IBinder _a, String _w,
                 int _r, Intent[] _i, String[] _it, int _f, SafeActivityOptions _o, int _userId) {
             type = _t;
             packageName = _p;
+            featureId = _featureId;
             activity = _a;
             who = _w;
             requestCode = _r;
@@ -140,6 +144,9 @@
                 if (!Objects.equals(packageName, other.packageName)) {
                     return false;
                 }
+                if (!Objects.equals(featureId, other.featureId)) {
+                    return false;
+                }
                 if (activity != other.activity) {
                     return false;
                 }
@@ -175,7 +182,8 @@
         }
 
         public String toString() {
-            return "Key{" + typeName() + " pkg=" + packageName
+            return "Key{" + typeName()
+                + " pkg=" + packageName + (featureId != null ? "/" + featureId : "")
                 + " intent="
                 + (requestIntent != null
                         ? requestIntent.toShortString(false, true, false, false) : "<null>")
@@ -403,19 +411,20 @@
 
                         if (key.allIntents != null && key.allIntents.length > 1) {
                             res = controller.mAtmInternal.startActivitiesInPackage(
-                                    uid, callingPid, callingUid, key.packageName, allIntents,
-                                    allResolvedTypes, resultTo, mergedOptions, userId,
+                                    uid, callingPid, callingUid, key.packageName, key.featureId,
+                                    allIntents, allResolvedTypes, resultTo, mergedOptions, userId,
                                     false /* validateIncomingUser */,
                                     this /* originatingPendingIntent */,
                                     mAllowBgActivityStartsForActivitySender.contains(whitelistToken));
                         } else {
-                            res = controller.mAtmInternal.startActivityInPackage(
-                                    uid, callingPid, callingUid, key.packageName, finalIntent,
+                            res = controller.mAtmInternal.startActivityInPackage(uid, callingPid,
+                                    callingUid, key.packageName, key.featureId, finalIntent,
                                     resolvedType, resultTo, resultWho, requestCode, 0,
                                     mergedOptions, userId, null, "PendingIntentRecord",
                                     false /* validateIncomingUser */,
                                     this /* originatingPendingIntent */,
-                                    mAllowBgActivityStartsForActivitySender.contains(whitelistToken));
+                                    mAllowBgActivityStartsForActivitySender.contains(
+                                            whitelistToken));
                         }
                     } catch (RuntimeException e) {
                         Slog.w(TAG, "Unable to send startActivity intent", e);
@@ -430,11 +439,12 @@
                         // If a completion callback has been requested, require
                         // that the broadcast be delivered synchronously
                         int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName,
-                                uid, callingUid, callingPid, finalIntent, resolvedType,
-                                finishedReceiver, code, null, null, requiredPermission, options,
-                                (finishedReceiver != null), false, userId,
+                                key.featureId, uid, callingUid, callingPid, finalIntent,
+                                resolvedType, finishedReceiver, code, null, null,
+                                requiredPermission, options, (finishedReceiver != null), false,
+                                userId,
                                 mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken)
-                                || allowTrampoline);
+                                        || allowTrampoline);
                         if (sent == ActivityManager.BROADCAST_SUCCESS) {
                             sendFinish = false;
                         }
@@ -447,7 +457,7 @@
                     try {
                         controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType,
                                 key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
-                                key.packageName, userId,
+                                key.packageName, key.featureId, userId,
                                 mAllowBgActivityStartsForServiceSender.contains(whitelistToken)
                                 || allowTrampoline);
                     } catch (RuntimeException e) {
@@ -496,6 +506,7 @@
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("uid="); pw.print(uid);
                 pw.print(" packageName="); pw.print(key.packageName);
+                pw.print(" featureId="); pw.print(key.featureId);
                 pw.print(" type="); pw.print(key.typeName());
                 pw.print(" flags=0x"); pw.println(Integer.toHexString(key.flags));
         if (key.activity != null || key.who != null) {
@@ -545,6 +556,10 @@
         sb.append(Integer.toHexString(System.identityHashCode(this)));
         sb.append(' ');
         sb.append(key.packageName);
+        if (key.featureId != null) {
+            sb.append('/');
+            sb.append(key.featureId);
+        }
         sb.append(' ');
         sb.append(key.typeName());
         if (whitelistDuration != null) {
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index beb0e47..747e8a8 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -108,8 +108,8 @@
 
         mIntent.setComponent(componentName);
         synchronized (mService) {
-            mService.broadcastIntentLocked(null, null, mIntent, null, this, 0, null, null, null,
-                    AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
+            mService.broadcastIntentLocked(null, null, null, mIntent, null, this, 0, null, null,
+                    null, AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
                     Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId);
         }
     }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index dcada89..abe0dd5 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1577,7 +1577,19 @@
             // For DownloadProviders and MTP: To grant access to /sdcard/Android/
             // And a special case for the FUSE daemon since it runs an MTP server and should have
             // access to Android/
+            // Note that we must add in the user id, because sdcardfs synthesizes this permission
+            // based on the user
             gidList.add(UserHandle.getUid(UserHandle.getUserId(uid), Process.SDCARD_RW_GID));
+
+            // For devices without sdcardfs, these GIDs are needed instead; note that we
+            // consciously don't add the user_id in the GID, since these apps are anyway
+            // isolated to only their own user
+            gidList.add(Process.EXT_DATA_RW_GID);
+            gidList.add(Process.EXT_OBB_RW_GID);
+        }
+        if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) {
+            // For devices without sdcardfs, this GID is needed to allow installers access to OBBs
+            gidList.add(Process.EXT_OBB_RW_GID);
         }
         if (mountExternal == Zygote.MOUNT_EXTERNAL_PASS_THROUGH) {
             // For the FUSE daemon: To grant access to the lower filesystem.
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index b2d0441..88eb885 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2448,10 +2448,10 @@
                 int realCallingPid, @UserIdInt int userId) {
             // TODO b/64165549 Verify that mLock is not held before calling AMS methods
             synchronized (mService) {
-                return mService.broadcastIntentLocked(null, null, intent, resolvedType, resultTo,
-                        resultCode, resultData, resultExtras, requiredPermissions, appOp, bOptions,
-                        ordered, sticky, callingPid, callingUid, realCallingUid, realCallingPid,
-                        userId);
+                return mService.broadcastIntentLocked(null, null, null, intent, resolvedType,
+                        resultTo, resultCode, resultData, resultExtras, requiredPermissions, appOp,
+                        bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
+                        realCallingPid, userId);
             }
         }
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index fea1c10..97a8b87 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -21,7 +21,6 @@
 import static android.media.AudioManager.RINGER_MODE_SILENT;
 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
 import static android.media.AudioManager.STREAM_SYSTEM;
-import static android.media.audiopolicy.AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE;
 import static android.os.Process.FIRST_APPLICATION_UID;
 import static android.provider.Settings.Secure.VOLUME_HUSH_MUTE;
 import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
@@ -91,7 +90,6 @@
 import android.media.VolumePolicy;
 import android.media.audiofx.AudioEffect;
 import android.media.audiopolicy.AudioMix;
-import android.media.audiopolicy.AudioMixingRule.AudioMixMatchCriterion;
 import android.media.audiopolicy.AudioPolicy;
 import android.media.audiopolicy.AudioPolicyConfig;
 import android.media.audiopolicy.AudioProductStrategy;
@@ -6826,8 +6824,9 @@
 
         boolean requireValidProjection = false;
         boolean requireCaptureAudioOrMediaOutputPerm = false;
-        boolean requireVoiceComunicationOutputPerm = false;
         boolean requireModifyRouting = false;
+        ArrayList<AudioMix> voiceCommunicationCaptureMixes = null;
+
 
         if (hasFocusAccess || isVolumeController) {
             requireModifyRouting |= true;
@@ -6836,23 +6835,29 @@
             requireModifyRouting |= true;
         }
         for (AudioMix mix : policyConfig.getMixes()) {
-            // If mix is trying to capture USAGE_VOICE_COMMUNICATION using playback capture
-            if (isVoiceCommunicationPlaybackCaptureMix(mix)) {
-                // then it must have CAPTURE_USAGE_VOICE_COMMUNICATION_OUTPUT permission
-                requireVoiceComunicationOutputPerm |= true;
-            }
-            // If mix is requesting privileged capture and is capturing at
-            // least one usage which is not USAGE_VOICE_COMMUNICATION.
-            if (mix.getRule().allowPrivilegedPlaybackCapture()
-                    && isNonVoiceCommunicationCaptureMix(mix)) {
+            // If mix is requesting privileged capture
+            if (mix.getRule().allowPrivilegedPlaybackCapture()) {
                 // then it must have CAPTURE_MEDIA_OUTPUT or CAPTURE_AUDIO_OUTPUT permission
                 requireCaptureAudioOrMediaOutputPerm |= true;
+
                 // and its format must be low quality enough
                 String error = mix.canBeUsedForPrivilegedCapture(mix.getFormat());
                 if (error != null) {
                     Log.e(TAG, error);
                     return false;
                 }
+
+                // If mix is trying to excplicitly capture USAGE_VOICE_COMMUNICATION
+                if (mix.containsMatchAttributeRuleForUsage(
+                        AudioAttributes.USAGE_VOICE_COMMUNICATION)) {
+                    // then it must have CAPTURE_USAGE_VOICE_COMMUNICATION_OUTPUT permission
+                    // Note that for UID, USERID or EXCLDUE rules, the capture will be silenced
+                    // in AudioPolicyMix
+                    if (voiceCommunicationCaptureMixes == null) {
+                        voiceCommunicationCaptureMixes = new ArrayList<AudioMix>();
+                    }
+                    voiceCommunicationCaptureMixes.add(mix);
+                }
             }
 
             // If mix is RENDER|LOOPBACK, then an audio MediaProjection is enough
@@ -6872,12 +6877,18 @@
             return false;
         }
 
-        if (requireVoiceComunicationOutputPerm
-                && !callerHasPermission(
-                        android.Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT)) {
-            Log.e(TAG, "Privileged audio capture for voice communication requires "
-                      + "CAPTURE_VOICE_COMMUNICATION_OUTPUT system permission");
-            return false;
+        if (voiceCommunicationCaptureMixes != null && voiceCommunicationCaptureMixes.size() > 0) {
+            if (!callerHasPermission(
+                    android.Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT)) {
+                Log.e(TAG, "Privileged audio capture for voice communication requires "
+                        + "CAPTURE_VOICE_COMMUNICATION_OUTPUT system permission");
+                return false;
+            }
+
+            // If permission check succeeded, we set the flag in each of the mixing rules
+            for (AudioMix mix : voiceCommunicationCaptureMixes) {
+                mix.getRule().setVoiceCommunicationCaptureAllowed(true);
+            }
         }
 
         if (requireValidProjection && !canProjectAudio(projection)) {
@@ -6893,41 +6904,6 @@
         return true;
     }
 
-    /**
-    * Checks whether a given AudioMix is used for playback capture
-    * (has the ROUTE_FLAG_LOOP_BACK_RENDER flag) and has a matching
-    * criterion for USAGE_VOICE_COMMUNICATION.
-    */
-    private boolean isVoiceCommunicationPlaybackCaptureMix(AudioMix mix) {
-        if (mix.getRouteFlags() != mix.ROUTE_FLAG_LOOP_BACK_RENDER) {
-            return false;
-        }
-
-        for (AudioMixMatchCriterion criterion : mix.getRule().getCriteria()) {
-            if (criterion.getRule() == RULE_MATCH_ATTRIBUTE_USAGE
-                    && criterion.getAudioAttributes().getUsage()
-                    == AudioAttributes.USAGE_VOICE_COMMUNICATION) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-    * Checks whether a given AudioMix has a matching
-    * criterion for a usage which is not USAGE_VOICE_COMMUNICATION.
-    */
-    private boolean isNonVoiceCommunicationCaptureMix(AudioMix mix) {
-        for (AudioMixMatchCriterion criterion : mix.getRule().getCriteria()) {
-            if (criterion.getRule() == RULE_MATCH_ATTRIBUTE_USAGE
-                    && criterion.getAudioAttributes().getUsage()
-                    != AudioAttributes.USAGE_VOICE_COMMUNICATION) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     private boolean callerHasPermission(String permission) {
         return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
     }
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index 1a1845a..cbfa87f 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -274,8 +274,18 @@
         return hwSel;
     }
 
-    static @NonNull ProgramSelector programSelectorFromHal(
+    private static boolean isEmpty(
             @NonNull android.hardware.broadcastradio.V2_0.ProgramSelector sel) {
+        if (sel.primaryId.type != 0) return false;
+        if (sel.primaryId.value != 0) return false;
+        if (sel.secondaryIds.size() != 0) return false;
+        return true;
+    }
+
+    static @Nullable ProgramSelector programSelectorFromHal(
+            @NonNull android.hardware.broadcastradio.V2_0.ProgramSelector sel) {
+        if (isEmpty(sel)) return null;
+
         ProgramSelector.Identifier[] secondaryIds = sel.secondaryIds.stream().
                 map(Convert::programIdentifierFromHal).map(Objects::requireNonNull).
                 toArray(ProgramSelector.Identifier[]::new);
@@ -363,7 +373,7 @@
                 collect(Collectors.toList());
 
         return new RadioManager.ProgramInfo(
-                programSelectorFromHal(info.selector),
+                Objects.requireNonNull(programSelectorFromHal(info.selector)),
                 programIdentifierFromHal(info.logicallyTunedTo),
                 programIdentifierFromHal(info.physicallyTunedTo),
                 relatedContent,
@@ -401,7 +411,7 @@
     public static @NonNull android.hardware.radio.Announcement announcementFromHal(
             @NonNull Announcement hwAnnouncement) {
         return new android.hardware.radio.Announcement(
-            programSelectorFromHal(hwAnnouncement.selector),
+            Objects.requireNonNull(programSelectorFromHal(hwAnnouncement.selector)),
             hwAnnouncement.type,
             vendorInfoFromHal(hwAnnouncement.vendorInfo)
         );
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index af8a366..5059a48 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -325,7 +325,7 @@
             mSlot = slot;
             int error = isValid();
             if (error == SUCCESS) {
-                Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
+                Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.toShortString());
                 switch (mType) {
                     case TYPE_NATT:
                         mNai.asyncChannel.sendMessage(
@@ -365,7 +365,8 @@
                     Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
                 }
             }
-            Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name() + ": " + reason);
+            Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.toShortString()
+                    + ": " + reason);
             switch (mStartedState) {
                 case NOT_STARTED:
                     // Remove the reference of the keepalive that meet error before starting,
@@ -476,7 +477,7 @@
     }
 
     public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
-        String networkName = (nai == null) ? "(null)" : nai.name();
+        final String networkName = NetworkAgentInfo.toShortString(nai);
         HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
         if (networkKeepalives == null) {
             Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName);
@@ -493,7 +494,7 @@
     }
 
     private void cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot) {
-        String networkName = (nai == null) ? "(null)" : nai.name();
+        final String networkName = NetworkAgentInfo.toShortString(nai);
         HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
         if (networkKeepalives == null) {
             Log.e(TAG, "Attempt to remove keepalive on nonexistent network " + networkName);
@@ -540,7 +541,7 @@
         } catch(NullPointerException e) {}
         if (ki == null) {
             Log.e(TAG, "Event " + message.what + "," + slot + "," + reason
-                    + " for unknown keepalive " + slot + " on " + nai.name());
+                    + " for unknown keepalive " + slot + " on " + nai.toShortString());
             return;
         }
 
@@ -562,7 +563,7 @@
         if (KeepaliveInfo.STARTING == ki.mStartedState) {
             if (SUCCESS == reason) {
                 // Keepalive successfully started.
-                Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
+                Log.d(TAG, "Started keepalive " + slot + " on " + nai.toShortString());
                 ki.mStartedState = KeepaliveInfo.STARTED;
                 try {
                     ki.mCallback.onStarted(slot);
@@ -570,14 +571,14 @@
                     Log.w(TAG, "Discarded onStarted(" + slot + ") callback");
                 }
             } else {
-                Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.name()
+                Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.toShortString()
                         + ": " + reason);
                 // The message indicated some error trying to start: do call handleStopKeepalive.
                 handleStopKeepalive(nai, slot, reason);
             }
         } else if (KeepaliveInfo.STOPPING == ki.mStartedState) {
             // The message indicated result of stopping : clean up keepalive slots.
-            Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.name()
+            Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.toShortString()
                     + " stopped: " + reason);
             ki.mStartedState = KeepaliveInfo.NOT_STARTED;
             cleanupStoppedKeepalive(nai, slot);
@@ -733,7 +734,7 @@
         pw.println("Socket keepalives:");
         pw.increaseIndent();
         for (NetworkAgentInfo nai : mKeepalives.keySet()) {
-            pw.println(nai.name());
+            pw.println(nai.toShortString());
             pw.increaseIndent();
             for (int slot : mKeepalives.get(nai).keySet()) {
                 KeepaliveInfo ki = mKeepalives.get(nai).get(slot);
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/services/core/java/com/android/server/connectivity/LingerMonitor.java
index 7071510..04c000f 100644
--- a/services/core/java/com/android/server/connectivity/LingerMonitor.java
+++ b/services/core/java/com/android/server/connectivity/LingerMonitor.java
@@ -200,8 +200,9 @@
         }
 
         if (DBG) {
-            Log.d(TAG, "Notifying switch from=" + fromNai.name() + " to=" + toNai.name() +
-                    " type=" + sNotifyTypeNames.get(notifyType, "unknown(" + notifyType + ")"));
+            Log.d(TAG, "Notifying switch from=" + fromNai.toShortString()
+                    + " to=" + toNai.toShortString()
+                    + " type=" + sNotifyTypeNames.get(notifyType, "unknown(" + notifyType + ")"));
         }
 
         mNotifications.put(fromNai.network.netId, toNai.network.netId);
@@ -222,10 +223,10 @@
     public void noteLingerDefaultNetwork(@NonNull final NetworkAgentInfo fromNai,
             @Nullable final NetworkAgentInfo toNai) {
         if (VDBG) {
-            Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.name() +
-                    " everValidated=" + fromNai.everValidated +
-                    " lastValidated=" + fromNai.lastValidated +
-                    " to=" + toNai.name());
+            Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.toShortString()
+                    + " everValidated=" + fromNai.everValidated
+                    + " lastValidated=" + fromNai.lastValidated
+                    + " to=" + toNai.toShortString());
         }
 
         // If we are currently notifying the user because the device switched to fromNai, now that
@@ -270,7 +271,8 @@
         // TODO: should we do this?
         if (everNotified(fromNai)) {
             if (VDBG) {
-                Log.d(TAG, "Not notifying handover from " + fromNai.name() + ", already notified");
+                Log.d(TAG, "Not notifying handover from " + fromNai.toShortString()
+                        + ", already notified");
             }
             return;
         }
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index f636d67..82465f8 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -174,7 +174,7 @@
         try {
             mNMService.registerObserver(this);
         } catch (RemoteException e) {
-            Slog.e(TAG, "Can't register interface observer for clat on " + mNetwork.name());
+            Slog.e(TAG, "Can't register iface observer for clat on " + mNetwork.toShortString());
             return;
         }
 
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 3cfe916..592be2f 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,7 +16,10 @@
 
 package com.android.server.connectivity;
 
+import static android.net.NetworkCapabilities.transportNamesOf;
+
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.IDnsResolver;
 import android.net.INetd;
@@ -372,7 +375,7 @@
             // Should only happen if the requestId wraps. If that happens lots of other things will
             // be broken as well.
             Log.wtf(TAG, String.format("Duplicate requestId for %s and %s on %s",
-                    networkRequest, existing, name()));
+                    networkRequest, existing, toShortString()));
             updateRequestCounts(REMOVE, existing);
         }
         mNetworkRequests.put(networkRequest.requestId, networkRequest);
@@ -542,11 +545,11 @@
             // Cannot happen. Once a request is lingering on a particular network, we cannot
             // re-linger it unless that network becomes the best for that request again, in which
             // case we should have unlingered it.
-            Log.wtf(TAG, this.name() + ": request " + request.requestId + " already lingered");
+            Log.wtf(TAG, toShortString() + ": request " + request.requestId + " already lingered");
         }
         final long expiryMs = now + duration;
         LingerTimer timer = new LingerTimer(request, expiryMs);
-        if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + this.name());
+        if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString());
         mLingerTimers.add(timer);
         mLingerTimerForRequest.put(request.requestId, timer);
     }
@@ -558,7 +561,7 @@
     public boolean unlingerRequest(NetworkRequest request) {
         LingerTimer timer = mLingerTimerForRequest.get(request.requestId);
         if (timer != null) {
-            if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + this.name());
+            if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString());
             mLingerTimers.remove(timer);
             mLingerTimerForRequest.remove(request.requestId);
             return true;
@@ -645,9 +648,16 @@
                 + "}";
     }
 
-    public String name() {
-        return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" +
-                networkInfo.getSubtypeName() + ") - " + Objects.toString(network) + "]";
+    /**
+     * Show a short string representing a Network.
+     *
+     * This is often not enough for debugging purposes for anything complex, but the full form
+     * is very long and hard to read, so this is useful when there isn't a lot of ambiguity.
+     * This represents the network with something like "[100 WIFI|VPN]" or "[108 MOBILE]".
+     */
+    public String toShortString() {
+        return "[" + network.netId + " "
+                + transportNamesOf(networkCapabilities.getTransportTypes()) + "]";
     }
 
     // Enables sorting in descending order of score.
@@ -655,4 +665,12 @@
     public int compareTo(NetworkAgentInfo other) {
         return other.getCurrentScore() - getCurrentScore();
     }
+
+    /**
+     * Null-guarding version of NetworkAgentInfo#toShortString()
+     */
+    @NonNull
+    public static String toShortString(@Nullable final NetworkAgentInfo nai) {
+        return null != nai ? nai.toShortString() : "[null]";
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java
new file mode 100644
index 0000000..d0aabf95
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkRequest;
+
+import java.util.Collection;
+
+/**
+ * A class that knows how to find the best network matching a request out of a list of networks.
+ */
+public class NetworkRanker {
+    public NetworkRanker() { }
+
+    /**
+     * Find the best network satisfying this request among the list of passed networks.
+     */
+    // Almost equivalent to Collections.max(nais), but allows returning null if no network
+    // satisfies the request.
+    @Nullable
+    public NetworkAgentInfo getBestNetwork(@NonNull final NetworkRequest request,
+            @NonNull final Collection<NetworkAgentInfo> nais) {
+        NetworkAgentInfo bestNetwork = null;
+        int bestScore = Integer.MIN_VALUE;
+        for (final NetworkAgentInfo nai : nais) {
+            if (!nai.satisfies(request)) continue;
+            if (nai.getCurrentScore() > bestScore) {
+                bestNetwork = nai;
+                bestScore = nai.getCurrentScore();
+            }
+        }
+        return bestNetwork;
+    }
+}
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 8358884..3de5cf1 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -62,7 +62,8 @@
     public abstract void transferToRoute(String sessionId, String routeId);
 
     public abstract void sendControlRequest(String routeId, Intent request);
-    public abstract void requestSetVolume(String routeId, int volume);
+    public abstract void setRouteVolume(String routeId, int volume);
+    public abstract void setSessionVolume(String sessionId, int volume);
 
     @NonNull
     public String getUniqueId() {
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index dd536ec..c1ea697 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -131,9 +131,17 @@
     }
 
     @Override
-    public void requestSetVolume(String routeId, int volume) {
+    public void setRouteVolume(String routeId, int volume) {
         if (mConnectionReady) {
-            mActiveConnection.requestSetVolume(routeId, volume);
+            mActiveConnection.setRouteVolume(routeId, volume);
+            updateBinding();
+        }
+    }
+
+    @Override
+    public void setSessionVolume(String sessionId, int volume) {
+        if (mConnectionReady) {
+            mActiveConnection.setSessionVolume(sessionId, volume);
             updateBinding();
         }
     }
@@ -456,7 +464,7 @@
             try {
                 mProvider.requestCreateSession(packageName, routeId, requestId, sessionHints);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to create a session.", ex);
+                Slog.e(TAG, "requestCreateSession: Failed to deliver request.");
             }
         }
 
@@ -464,7 +472,7 @@
             try {
                 mProvider.releaseSession(sessionId);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to release a session.", ex);
+                Slog.e(TAG, "releaseSession: Failed to deliver request.");
             }
         }
 
@@ -472,7 +480,7 @@
             try {
                 mProvider.updateDiscoveryPreference(discoveryPreference);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "updateDiscoveryPreference(): Failed to deliver request.");
+                Slog.e(TAG, "updateDiscoveryPreference: Failed to deliver request.");
             }
         }
 
@@ -480,7 +488,7 @@
             try {
                 mProvider.selectRoute(sessionId, routeId);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to select a route for a session.", ex);
+                Slog.e(TAG, "selectRoute: Failed to deliver request.", ex);
             }
         }
 
@@ -488,7 +496,7 @@
             try {
                 mProvider.deselectRoute(sessionId, routeId);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to deselect a route from a session.", ex);
+                Slog.e(TAG, "deselectRoute: Failed to deliver request.", ex);
             }
         }
 
@@ -496,7 +504,7 @@
             try {
                 mProvider.transferToRoute(sessionId, routeId);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to transfer a session to a route.", ex);
+                Slog.e(TAG, "transferToRoute: Failed to deliver request.", ex);
             }
         }
 
@@ -504,15 +512,23 @@
             try {
                 mProvider.notifyControlRequestSent(routeId, request);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to send control request.", ex);
+                Slog.e(TAG, "sendControlRequest: Failed to deliver request.", ex);
             }
         }
 
-        public void requestSetVolume(String routeId, int volume) {
+        public void setRouteVolume(String routeId, int volume) {
             try {
-                mProvider.requestSetVolume(routeId, volume);
+                mProvider.setRouteVolume(routeId, volume);
             } catch (RemoteException ex) {
-                Slog.e(TAG, "Failed to deliver request to request set volume.", ex);
+                Slog.e(TAG, "setRouteVolume: Failed to deliver request.", ex);
+            }
+        }
+
+        public void setSessionVolume(String sessionId, int volume) {
+            try {
+                mProvider.setSessionVolume(sessionId, volume);
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "setSessionVolume: Failed to deliver request.", ex);
             }
         }
 
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index b113322..2096531 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -320,14 +320,29 @@
         }
     }
 
-    public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) {
+    public void setRouteVolume2(IMediaRouter2Client client,
+            MediaRoute2Info route, int volume) {
         Objects.requireNonNull(client, "client must not be null");
         Objects.requireNonNull(route, "route must not be null");
 
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                requestSetVolumeLocked(client, route, volume);
+                setRouteVolumeLocked(client, route, volume);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void setSessionVolume2(IMediaRouter2Client client, String sessionId, int volume) {
+        Objects.requireNonNull(client, "client must not be null");
+        Objects.requireNonNull(sessionId, "sessionId must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                setSessionVolumeLocked(client, sessionId, volume);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -346,7 +361,7 @@
         }
     }
 
-    public void requestSetVolume2Manager(IMediaRouter2Manager manager,
+    public void setRouteVolume2Manager(IMediaRouter2Manager manager,
             MediaRoute2Info route, int volume) {
         Objects.requireNonNull(manager, "manager must not be null");
         Objects.requireNonNull(route, "route must not be null");
@@ -354,7 +369,22 @@
         final long token = Binder.clearCallingIdentity();
         try {
             synchronized (mLock) {
-                requestSetVolumeLocked(manager, route, volume);
+                setRouteVolumeLocked(manager, route, volume);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void setSessionVolume2Manager(IMediaRouter2Manager manager,
+            String sessionId, int volume) {
+        Objects.requireNonNull(manager, "manager must not be null");
+        Objects.requireNonNull(sessionId, "sessionId must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                setSessionVolumeLocked(manager, sessionId, volume);
             }
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -587,18 +617,30 @@
         }
     }
 
-    private void requestSetVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
+    private void setRouteVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
             int volume) {
         final IBinder binder = client.asBinder();
         Client2Record clientRecord = mAllClientRecords.get(binder);
 
         if (clientRecord != null) {
             clientRecord.mUserRecord.mHandler.sendMessage(
-                    obtainMessage(UserHandler::requestSetVolume,
+                    obtainMessage(UserHandler::setRouteVolume,
                             clientRecord.mUserRecord.mHandler, route, volume));
         }
     }
 
+    private void setSessionVolumeLocked(IMediaRouter2Client client, String sessionId,
+            int volume) {
+        final IBinder binder = client.asBinder();
+        Client2Record clientRecord = mAllClientRecords.get(binder);
+
+        if (clientRecord != null) {
+            clientRecord.mUserRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::setSessionVolume,
+                            clientRecord.mUserRecord.mHandler, sessionId, volume));
+        }
+    }
+
     private void registerManagerLocked(IMediaRouter2Manager manager,
             int uid, int pid, String packageName, int userId, boolean trusted) {
         final IBinder binder = manager.asBinder();
@@ -660,18 +702,30 @@
         }
     }
 
-    private void requestSetVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route,
+    private void setRouteVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route,
             int volume) {
         final IBinder binder = manager.asBinder();
         ManagerRecord managerRecord = mAllManagerRecords.get(binder);
 
         if (managerRecord != null) {
             managerRecord.mUserRecord.mHandler.sendMessage(
-                    obtainMessage(UserHandler::requestSetVolume,
+                    obtainMessage(UserHandler::setRouteVolume,
                             managerRecord.mUserRecord.mHandler, route, volume));
         }
     }
 
+    private void setSessionVolumeLocked(IMediaRouter2Manager manager, String sessionId,
+            int volume) {
+        final IBinder binder = manager.asBinder();
+        ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+        if (managerRecord != null) {
+            managerRecord.mUserRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::setSessionVolume,
+                            managerRecord.mUserRecord.mHandler, sessionId, volume));
+        }
+    }
+
     private List<RoutingSessionInfo> getActiveSessionsLocked(IMediaRouter2Manager manager) {
         final IBinder binder = manager.asBinder();
         ManagerRecord managerRecord = mAllManagerRecords.get(binder);
@@ -1399,13 +1453,23 @@
             }
         }
 
-        private void requestSetVolume(MediaRoute2Info route, int volume) {
+        private void setRouteVolume(MediaRoute2Info route, int volume) {
             final MediaRoute2Provider provider = findProvider(route.getProviderId());
             if (provider != null) {
-                provider.requestSetVolume(route.getOriginalId(), volume);
+                provider.setRouteVolume(route.getOriginalId(), volume);
             }
         }
 
+        private void setSessionVolume(String sessionId, int volume) {
+            final MediaRoute2Provider provider = findProvider(getProviderId(sessionId));
+            if (provider == null) {
+                Slog.w(TAG, "setSessionVolume: couldn't find provider for session "
+                        + "id=" + sessionId);
+                return;
+            }
+            provider.setSessionVolume(getOriginalId(sessionId), volume);
+        }
+
         private List<IMediaRouter2Client> getClients() {
             final List<IMediaRouter2Client> clients = new ArrayList<>();
             MediaRouter2ServiceImpl service = mServiceRef.get();
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 57f0328..b38e47a 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -533,15 +533,29 @@
 
     // Binder call
     @Override
-    public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) {
-        mService2.requestSetVolume2(client, route, volume);
+    public void setRouteVolume2(IMediaRouter2Client client,
+            MediaRoute2Info route, int volume) {
+        mService2.setRouteVolume2(client, route, volume);
     }
 
     // Binder call
     @Override
-    public void requestSetVolume2Manager(IMediaRouter2Manager manager,
+    public void setSessionVolume2(IMediaRouter2Client client, String sessionId, int volume) {
+        mService2.setSessionVolume2(client, sessionId, volume);
+    }
+
+    // Binder call
+    @Override
+    public void setRouteVolume2Manager(IMediaRouter2Manager manager,
             MediaRoute2Info route, int volume) {
-        mService2.requestSetVolume2Manager(manager, route, volume);
+        mService2.setRouteVolume2Manager(manager, route, volume);
+    }
+
+    // Binder call
+    @Override
+    public void setSessionVolume2Manager(IMediaRouter2Manager manager,
+            String sessionId, int volume) {
+        mService2.setSessionVolume2Manager(manager, sessionId, volume);
     }
 
     // Binder call
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index b5dcea8..18383c4 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -42,6 +42,7 @@
 import android.util.Log;
 
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 
 import java.util.Collections;
 import java.util.List;
@@ -67,6 +68,8 @@
             SystemMediaRoute2Provider.class.getPackageName$(),
             SystemMediaRoute2Provider.class.getName());
 
+    @GuardedBy("mLock")
+    private String mSelectedRouteId;
     MediaRoute2Info mDefaultRoute;
     @NonNull List<MediaRoute2Info> mBluetoothRoutes = Collections.EMPTY_LIST;
     final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
@@ -153,9 +156,17 @@
     public void sendControlRequest(@NonNull String routeId, @NonNull Intent request) {
     }
 
-    //TODO: implement method
     @Override
-    public void requestSetVolume(String routeId, int volume) {
+    public void setRouteVolume(String routeId, int volume) {
+        if (!TextUtils.equals(routeId, mSelectedRouteId)) {
+            return;
+        }
+        mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+    }
+
+    @Override
+    public void setSessionVolume(String sessionId, int volume) {
+        // Do nothing since we don't support grouping volume yet.
     }
 
     private void initializeDefaultRoute() {
@@ -241,18 +252,16 @@
                 SYSTEM_SESSION_ID, "" /* clientPackageName */)
                 .setSystemSession(true);
         String activeBtDeviceAddress = mBtRouteProvider.getActiveDeviceAddress();
+        mSelectedRouteId = TextUtils.isEmpty(activeBtDeviceAddress) ? mDefaultRoute.getId()
+                : activeBtDeviceAddress;
+        builder.addSelectedRoute(mSelectedRouteId);
 
         if (!TextUtils.isEmpty(activeBtDeviceAddress)) {
-            // Bluetooth route. Set the route ID with the device's address.
-            builder.addSelectedRoute(activeBtDeviceAddress);
             builder.addTransferrableRoute(mDefaultRoute.getId());
-        } else {
-            // Default device
-            builder.addSelectedRoute(mDefaultRoute.getId());
         }
 
         for (MediaRoute2Info route : mBluetoothRoutes) {
-            if (!TextUtils.equals(activeBtDeviceAddress, route.getId())) {
+            if (!TextUtils.equals(mSelectedRouteId, route.getId())) {
                 builder.addTransferrableRoute(route.getId());
             }
         }
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 1dcff07..bde9ee2 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1610,6 +1610,22 @@
             pw.decreaseIndent();
             pw.println();
 
+            pw.println("Stats Providers:");
+            pw.increaseIndent();
+            invokeForAllStatsProviderCallbacks((cb) -> {
+                pw.println(cb.mTag + " Xt:");
+                pw.increaseIndent();
+                pw.print(cb.getCachedStats(STATS_PER_IFACE).toString());
+                pw.decreaseIndent();
+                if (includeUid) {
+                    pw.println(cb.mTag + " Uid:");
+                    pw.increaseIndent();
+                    pw.print(cb.getCachedStats(STATS_PER_UID).toString());
+                    pw.decreaseIndent();
+                }
+            });
+            pw.decreaseIndent();
+
             pw.println("Dev stats:");
             pw.increaseIndent();
             mDevRecorder.dumpLocked(pw, fullHistory);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c8bc1b0..916b63b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -94,6 +94,9 @@
 
 import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_MISSING;
 import static com.android.internal.util.FrameworkStatsLog.BUBBLE_DEVELOPER_ERROR_REPORTED__ERROR__ACTIVITY_INFO_NOT_RESIZABLE;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
 import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
@@ -129,6 +132,7 @@
 import android.app.PendingIntent;
 import android.app.Person;
 import android.app.RemoteInput;
+import android.app.StatsManager;
 import android.app.StatusBarManager;
 import android.app.UriGrantsManager;
 import android.app.admin.DeviceAdminInfo;
@@ -221,6 +225,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.StatsEvent;
 import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
 import android.view.accessibility.AccessibilityEvent;
@@ -474,6 +479,8 @@
     private AppOpsManager mAppOps;
     private UsageStatsManagerInternal mAppUsageStats;
     private DevicePolicyManagerInternal mDpm;
+    private StatsManager mStatsManager;
+    private StatsPullAtomCallbackImpl mPullAtomCallback;
 
     private Archive mArchive;
 
@@ -1894,7 +1901,8 @@
             ActivityManager activityManager, GroupHelper groupHelper, IActivityManager am,
             UsageStatsManagerInternal appUsageStats, DevicePolicyManagerInternal dpm,
             IUriGrantsManager ugm, UriGrantsManagerInternal ugmInternal, AppOpsManager appOps,
-            UserManager userManager, NotificationHistoryManager historyManager) {
+            UserManager userManager, NotificationHistoryManager historyManager,
+            StatsManager statsManager) {
         mHandler = handler;
         Resources resources = getContext().getResources();
         mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(),
@@ -2054,6 +2062,7 @@
         mStripRemoteViewsSizeBytes = getContext().getResources().getInteger(
                 com.android.internal.R.integer.config_notificationStripRemoteViewSizeBytes);
 
+        mStatsManager = statsManager;
     }
 
     @Override
@@ -2097,7 +2106,9 @@
                 LocalServices.getService(UriGrantsManagerInternal.class),
                 (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE),
                 getContext().getSystemService(UserManager.class),
-                new NotificationHistoryManager(getContext(), handler));
+                new NotificationHistoryManager(getContext(), handler),
+                mStatsManager = (StatsManager) getContext().getSystemService(
+                        Context.STATS_MANAGER));
 
         // register for various Intents
         IntentFilter filter = new IntentFilter();
@@ -2172,6 +2183,57 @@
         }
     }
 
+    private void registerNotificationPreferencesPullers() {
+        mPullAtomCallback = new StatsPullAtomCallbackImpl();
+        mStatsManager.registerPullAtomCallback(
+                PACKAGE_NOTIFICATION_PREFERENCES,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                mPullAtomCallback
+        );
+        mStatsManager.registerPullAtomCallback(
+                PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                mPullAtomCallback
+        );
+        mStatsManager.registerPullAtomCallback(
+                PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES,
+                null, // use default PullAtomMetadata values
+                BackgroundThread.getExecutor(),
+                mPullAtomCallback
+        );
+    }
+
+    private class StatsPullAtomCallbackImpl implements StatsManager.StatsPullAtomCallback {
+        @Override
+        public int onPullAtom(int atomTag, List<StatsEvent> data) {
+            switch (atomTag) {
+                case PACKAGE_NOTIFICATION_PREFERENCES:
+                case PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES:
+                case PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES:
+                    return pullNotificationStates(atomTag, data);
+                default:
+                    throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
+            }
+        }
+    }
+
+    private int pullNotificationStates(int atomTag, List<StatsEvent> data) {
+        switch(atomTag) {
+            case PACKAGE_NOTIFICATION_PREFERENCES:
+                mPreferencesHelper.pullPackagePreferencesStats(data);
+                break;
+            case PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES:
+                mPreferencesHelper.pullPackageChannelPreferencesStats(data);
+                break;
+            case PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES:
+                mPreferencesHelper.pullPackageChannelGroupPreferencesStats(data);
+                break;
+        }
+        return StatsManager.PULL_SUCCESS;
+    }
+
     private GroupHelper getGroupHelper() {
         mAutoGroupAtCount =
                 getContext().getResources().getInteger(R.integer.config_autoGroupAtCount);
@@ -2250,6 +2312,7 @@
             mRoleObserver.init();
             mLauncherAppsService =
                     (LauncherApps) getContext().getSystemService(Context.LAUNCHER_APPS_SERVICE);
+            registerNotificationPreferencesPullers();
         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
             // This observer will force an update when observe is called, causing us to
             // bind to listener services.
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index fc2d9e7..2f78542 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -27,6 +27,7 @@
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
 
@@ -266,5 +267,39 @@
         int getInstanceId() {
             return (r.getSbn().getInstanceId() == null ? 0 : r.getSbn().getInstanceId().getId());
         }
+
+        /**
+         * @return Small hash of the notification ID, and tag (if present).
+         */
+        int getNotificationIdHash() {
+            return smallHash(Objects.hashCode(r.getSbn().getTag()) ^ r.getSbn().getId());
+        }
+
+        /**
+         * @return Small hash of the channel ID, if present, or 0 otherwise.
+         */
+        int getChannelIdHash() {
+            return smallHash(Objects.hashCode(r.getSbn().getNotification().getChannelId()));
+        }
+
+        /**
+         * @return Small hash of the group ID, respecting group override if present. 0 otherwise.
+         */
+        int getGroupIdHash() {
+            return smallHash(Objects.hashCode(r.getSbn().getGroup()));
+        }
+
+        // "Small" hashes will be in the range [0, MAX_HASH).
+        static final int MAX_HASH = (1 << 13);
+
+        /**
+         * Maps in to the range [0, MAX_HASH), keeping similar values distinct.
+         * @param in An arbitrary integer.
+         * @return in mod MAX_HASH, signs chosen to stay in the range [0, MAX_HASH).
+         */
+        @VisibleForTesting
+        static int smallHash(int in) {
+            return Math.floorMod(in, MAX_HASH);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 015d280..bb23d1e 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -40,28 +40,27 @@
                 /* int32 uid = 2 */ r.getUid(),
                 /* string package_name = 3 */ r.getSbn().getPackageName(),
                 /* int32 instance_id = 4 */ p.getInstanceId(),
-                /* int32 notification_id = 5 */ r.getSbn().getId(),
-                /* string notification_tag = 6 */ r.getSbn().getTag(),
-                /* string channel_id = 7 */ r.getSbn().getChannelIdLogTag(),
-                /* string group_id = 8 */ r.getSbn().getGroupLogTag(),
-                /* int32 group_instance_id = 9 */ 0, // TODO generate and fill instance ids
-                /* bool is_group_summary = 10 */ r.getSbn().getNotification().isGroupSummary(),
-                /* string category = 11 */ r.getSbn().getNotification().category,
-                /* int32 style = 12 */ p.getStyle(),
-                /* int32 num_people = 13 */ p.getNumPeople(),
-                /* int32 position = 14 */ position,
-                /* android.stats.sysui.NotificationImportance importance = 15 */ r.getImportance(),
-                /* int32 alerting = 16 */ buzzBeepBlink,
-                /* NotificationImportanceExplanation importance_source = 17 */
+                /* int32 notification_id_hash = 5 */ p.getNotificationIdHash(),
+                /* int32 channel_id_hash = 6 */ p.getChannelIdHash(),
+                /* string group_id_hash = 7 */ p.getGroupIdHash(),
+                /* int32 group_instance_id = 8 */ 0, // TODO generate and fill instance ids
+                /* bool is_group_summary = 9 */ r.getSbn().getNotification().isGroupSummary(),
+                /* string category = 10 */ r.getSbn().getNotification().category,
+                /* int32 style = 11 */ p.getStyle(),
+                /* int32 num_people = 12 */ p.getNumPeople(),
+                /* int32 position = 13 */ position,
+                /* android.stats.sysui.NotificationImportance importance = 14 */ r.getImportance(),
+                /* int32 alerting = 15 */ buzzBeepBlink,
+                /* NotificationImportanceExplanation importance_source = 16 */
                 r.getImportanceExplanationCode(),
-                /* android.stats.sysui.NotificationImportance importance_initial = 18 */
+                /* android.stats.sysui.NotificationImportance importance_initial = 17 */
                 r.getInitialImportance(),
-                /* NotificationImportanceExplanation importance_initial_source = 19 */
+                /* NotificationImportanceExplanation importance_initial_source = 18 */
                 r.getInitialImportanceExplanationCode(),
-                /* android.stats.sysui.NotificationImportance importance_asst = 20 */
+                /* android.stats.sysui.NotificationImportance importance_asst = 19 */
                 r.getAssistantImportance(),
-                /* int32 assistant_hash = 21 */ p.getAssistantHash(),
-                /* float assistant_ranking_score = 22 */ 0  // TODO connect up ranking score
+                /* int32 assistant_hash = 20 */ p.getAssistantHash(),
+                /* float assistant_ranking_score = 21 */ 0 // TODO connect up ranking score
         );
     }
 
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index a244c48..9f8362c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -20,6 +20,10 @@
 import static android.app.NotificationManager.IMPORTANCE_NONE;
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -46,6 +50,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
+import android.util.StatsEvent;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
@@ -80,6 +85,10 @@
     @VisibleForTesting
     static final int NOTIFICATION_CHANNEL_COUNT_LIMIT = 5000;
 
+    private static final int NOTIFICATION_PREFERENCES_PULL_LIMIT = 1000;
+    private static final int NOTIFICATION_CHANNEL_PULL_LIMIT = 2000;
+    private static final int NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT = 1000;
+
     @VisibleForTesting
     static final String TAG_RANKING = "ranking";
     private static final String TAG_PACKAGE = "package";
@@ -1682,6 +1691,88 @@
         }
     }
 
+    /**
+     * Fills out {@link PackageNotificationPreferences} proto and wraps it in a {@link StatsEvent}.
+     */
+    public void pullPackagePreferencesStats(List<StatsEvent> events) {
+        synchronized (mPackagePreferences) {
+            for (int i = 0; i < mPackagePreferences.size(); i++) {
+                if (i > NOTIFICATION_PREFERENCES_PULL_LIMIT) {
+                    break;
+                }
+                StatsEvent.Builder event = StatsEvent.newBuilder()
+                        .setAtomId(PACKAGE_NOTIFICATION_PREFERENCES);
+                final PackagePreferences r = mPackagePreferences.valueAt(i);
+                event.writeInt(r.uid);
+                event.writeInt(r.importance);
+                event.writeInt(r.visibility);
+                event.writeInt(r.lockedAppFields);
+                events.add(event.build());
+            }
+        }
+    }
+
+    /**
+     * Fills out {@link PackageNotificationChannelPreferences} proto and wraps it in a
+     * {@link StatsEvent}.
+     */
+    public void pullPackageChannelPreferencesStats(List<StatsEvent> events) {
+        synchronized (mPackagePreferences) {
+            int totalChannelsPulled = 0;
+            for (int i = 0; i < mPackagePreferences.size(); i++) {
+                if (totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) {
+                    break;
+                }
+                final PackagePreferences r = mPackagePreferences.valueAt(i);
+                for (NotificationChannel channel : r.channels.values()) {
+                    if (++totalChannelsPulled > NOTIFICATION_CHANNEL_PULL_LIMIT) {
+                        break;
+                    }
+                    StatsEvent.Builder event = StatsEvent.newBuilder()
+                            .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES);
+                    event.writeInt(r.uid);
+                    event.writeString(channel.getId());
+                    event.writeString(channel.getName().toString());
+                    event.writeString(channel.getDescription());
+                    event.writeInt(channel.getImportance());
+                    event.writeInt(channel.getUserLockedFields());
+                    event.writeBoolean(channel.isDeleted());
+                    events.add(event.build());
+                }
+            }
+        }
+    }
+
+    /**
+     * Fills out {@link PackageNotificationChannelGroupPreferences} proto and wraps it in a
+     * {@link StatsEvent}.
+     */
+    public void pullPackageChannelGroupPreferencesStats(List<StatsEvent> events) {
+        synchronized (mPackagePreferences) {
+            int totalGroupsPulled = 0;
+            for (int i = 0; i < mPackagePreferences.size(); i++) {
+                if (totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) {
+                    break;
+                }
+                final PackagePreferences r = mPackagePreferences.valueAt(i);
+                for (NotificationChannelGroup groupChannel : r.groups.values()) {
+                    if (++totalGroupsPulled > NOTIFICATION_CHANNEL_GROUP_PULL_LIMIT) {
+                        break;
+                    }
+                    StatsEvent.Builder event = StatsEvent.newBuilder()
+                            .setAtomId(PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES);
+                    event.writeInt(r.uid);
+                    event.writeString(groupChannel.getId());
+                    event.writeString(groupChannel.getName().toString());
+                    event.writeString(groupChannel.getDescription());
+                    event.writeBoolean(groupChannel.isBlocked());
+                    event.writeInt(groupChannel.getUserLockedFields());
+                    events.add(event.build());
+                }
+            }
+        }
+    }
+
     public JSONObject dumpJson(NotificationManagerService.DumpFilter filter) {
         JSONObject ranking = new JSONObject();
         JSONArray PackagePreferencess = new JSONArray();
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 3c31f6a..d75eb6d 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -913,9 +913,9 @@
                 }
 
                 try {
-                    ActivityManager.getService().broadcastIntent(null, intent, null, null, 0,
-                            null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false,
-                            userId);
+                    ActivityManager.getService().broadcastIntentWithFeature(null, null, intent,
+                            null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE,
+                            null, false, false, userId);
                 } catch (RemoteException e) {
                     // Intentionally left empty.
                 }
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index ad037bc..96d3cc1 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -37,6 +37,7 @@
 import android.content.Intent;
 import android.content.PermissionChecker;
 import android.content.pm.ActivityInfo;
+import android.content.pm.CrossProfileAppsInternal;
 import android.content.pm.ICrossProfileApps;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
@@ -44,9 +45,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.os.Binder;
-import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.stats.devicepolicy.DevicePolicyEnums;
@@ -54,12 +53,10 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.IAppOpsService;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
 import com.android.server.LocalServices;
-import com.android.server.appop.AppOpsService;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.util.ArrayList;
@@ -71,7 +68,7 @@
 
     private Context mContext;
     private Injector mInjector;
-    private AppOpsService mAppOpsService;
+    private AppOpsManager mAppOpsManager;
 
     public CrossProfileAppsServiceImpl(Context context) {
         this(context, new InjectorImpl(context));
@@ -81,6 +78,8 @@
     CrossProfileAppsServiceImpl(Context context, Injector injector) {
         mContext = context;
         mInjector = injector;
+
+        LocalServices.addService(CrossProfileAppsInternal.class, new LocalService());
     }
 
     @Override
@@ -102,6 +101,7 @@
     public void startActivityAsUser(
             IApplicationThread caller,
             String callingPackage,
+            String callingFeatureId,
             ComponentName component,
             @UserIdInt int userId,
             boolean launchMainActivity) throws RemoteException {
@@ -167,7 +167,7 @@
         launchIntent.setPackage(null);
         launchIntent.setComponent(component);
         mInjector.getActivityTaskManagerInternal().startActivityAsUser(
-                caller, callingPackage, launchIntent,
+                caller, callingPackage, callingFeatureId, launchIntent,
                 launchMainActivity
                         ? ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle()
                         : null,
@@ -178,6 +178,7 @@
     public void startActivityAsUserByIntent(
             IApplicationThread caller,
             String callingPackage,
+            String callingFeatureId,
             Intent intent,
             @UserIdInt int userId) throws RemoteException {
         Objects.requireNonNull(callingPackage);
@@ -214,8 +215,8 @@
 
         verifyActivityCanHandleIntent(launchIntent, callingUid, userId);
 
-        mInjector.getActivityTaskManagerInternal().startActivityAsUser(
-                caller, callingPackage, launchIntent, /* options= */ null, userId);
+        mInjector.getActivityTaskManagerInternal().startActivityAsUser(caller, callingPackage,
+                callingFeatureId, launchIntent, /* options= */ null, userId);
     }
 
     @Override
@@ -255,8 +256,8 @@
         Objects.requireNonNull(callingPackage);
         verifyCallingPackage(callingPackage);
 
-        final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(
-                callingPackage, mInjector.getCallingUserId());
+        final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(callingPackage,
+                mInjector.getCallingUserId());
         if (targetUserProfiles.isEmpty()) {
             return false;
         }
@@ -540,12 +541,11 @@
                 permission, uid, /* owningUid= */-1, /* exported= */ true);
     }
 
-    private AppOpsService getAppOpsService() {
-        if (mAppOpsService == null) {
-            IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
-            mAppOpsService = (AppOpsService) IAppOpsService.Stub.asInterface(b);
+    private AppOpsManager getAppOpsManager() {
+        if (mAppOpsManager == null) {
+            mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
         }
-        return mAppOpsService;
+        return mAppOpsManager;
     }
 
     private static class InjectorImpl implements Injector {
@@ -685,4 +685,30 @@
 
         int checkComponentPermission(String permission, int uid, int owningUid, boolean exported);
     }
+
+    class LocalService extends CrossProfileAppsInternal {
+        @Override
+        public boolean verifyPackageHasInteractAcrossProfilePermission(String packageName,
+                @UserIdInt int userId) throws PackageManager.NameNotFoundException {
+            final int uid = Objects.requireNonNull(
+                    mInjector.getPackageManager().getApplicationInfoAsUser(
+                            Objects.requireNonNull(packageName), /* flags= */ 0, userId)).uid;
+            return verifyUidHasInteractAcrossProfilePermission(packageName, uid);
+        }
+
+        @Override
+        public boolean verifyUidHasInteractAcrossProfilePermission(String packageName, int uid) {
+            Objects.requireNonNull(packageName);
+
+            return isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)
+                    || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, uid)
+                    || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_PROFILES, uid)
+                    || PermissionChecker.checkPermissionForPreflight(
+                            mContext,
+                            Manifest.permission.INTERACT_ACROSS_PROFILES,
+                            PermissionChecker.PID_UNKNOWN,
+                            uid,
+                            packageName) == PermissionChecker.PERMISSION_GRANTED;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 8333ae5..0b0f139 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -222,6 +222,7 @@
                         sanitizedIntent,
                         failureIntent,
                         requestObj.callingPackage,
+                        requestObj.callingFeatureId,
                         requestObj.verificationBundle,
                         requestObj.resolvedType,
                         requestObj.userId,
@@ -266,6 +267,7 @@
             @NonNull Intent sanitizedIntent,
             @Nullable Intent failureIntent,
             @NonNull String callingPackage,
+            @Nullable String callingFeatureId,
             @Nullable Bundle verificationBundle,
             @NonNull String resolvedType,
             int userId,
@@ -308,9 +310,10 @@
                         onFailureIntent = failureIntent;
                     }
                     final IIntentSender failureIntentTarget = ActivityManager.getService()
-                            .getIntentSender(
+                            .getIntentSenderWithFeature(
                                     ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
-                                    null /*token*/, null /*resultWho*/, 1 /*requestCode*/,
+                                    callingFeatureId, null /*token*/, null /*resultWho*/,
+                                    1 /*requestCode*/,
                                     new Intent[] { onFailureIntent },
                                     new String[] { resolvedType },
                                     PendingIntent.FLAG_CANCEL_CURRENT
@@ -328,9 +331,10 @@
             successIntent.setLaunchToken(token);
             try {
                 final IIntentSender successIntentTarget = ActivityManager.getService()
-                        .getIntentSender(
+                        .getIntentSenderWithFeature(
                                 ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
-                                null /*token*/, null /*resultWho*/, 0 /*requestCode*/,
+                                callingFeatureId, null /*token*/, null /*resultWho*/,
+                                0 /*requestCode*/,
                                 new Intent[] { successIntent },
                                 new String[] { resolvedType },
                                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index e9f84fd..f93c663 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -18,6 +18,7 @@
 
 import android.Manifest.permission;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -787,8 +788,9 @@
         }
 
         @Override
-        public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
-                Rect sourceBounds, Bundle startActivityOptions, int targetUserId) {
+        public boolean startShortcut(String callingPackage, String packageName, String featureId,
+                String shortcutId, Rect sourceBounds, Bundle startActivityOptions,
+                int targetUserId) {
             verifyCallingPackage(callingPackage);
             if (!canAccessProfile(targetUserId, "Cannot start activity")) {
                 return false;
@@ -812,15 +814,16 @@
             intents[0].setSourceBounds(sourceBounds);
 
             return startShortcutIntentsAsPublisher(
-                    intents, packageName, startActivityOptions, targetUserId);
+                    intents, packageName, featureId, startActivityOptions, targetUserId);
         }
 
         private boolean startShortcutIntentsAsPublisher(@NonNull Intent[] intents,
-                @NonNull String publisherPackage, Bundle startActivityOptions, int userId) {
+                @NonNull String publisherPackage, @Nullable String publishedFeatureId,
+                Bundle startActivityOptions, int userId) {
             final int code;
             try {
                 code = mActivityTaskManagerInternal.startActivitiesAsPackage(publisherPackage,
-                        userId, intents, startActivityOptions);
+                        publishedFeatureId, userId, intents, startActivityOptions);
                 if (ActivityManager.isStartResultSuccessful(code)) {
                     return true; // Success
                 } else {
@@ -875,8 +878,8 @@
 
         @Override
         public void startSessionDetailsActivityAsUser(IApplicationThread caller,
-                String callingPackage, SessionInfo sessionInfo, Rect sourceBounds,
-                Bundle opts, UserHandle userHandle) throws RemoteException {
+                String callingPackage, String callingFeatureId, SessionInfo sessionInfo,
+                Rect sourceBounds, Bundle opts, UserHandle userHandle) throws RemoteException {
             int userId = userHandle.getIdentifier();
             if (!canAccessProfile(userId, "Cannot start details activity")) {
                 return;
@@ -892,13 +895,13 @@
                             .authority(callingPackage).build());
             i.setSourceBounds(sourceBounds);
 
-            mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage, i, opts,
-                    userId);
+            mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
+                    callingFeatureId, i, opts, userId);
         }
 
         @Override
         public void startActivityAsUser(IApplicationThread caller, String callingPackage,
-                ComponentName component, Rect sourceBounds,
+                String callingFeatureId, ComponentName component, Rect sourceBounds,
                 Bundle opts, UserHandle user) throws RemoteException {
             if (!canAccessProfile(user.getIdentifier(), "Cannot start activity")) {
                 return;
@@ -952,12 +955,12 @@
                 Binder.restoreCallingIdentity(ident);
             }
             mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
-                    launchIntent, opts, user.getIdentifier());
+                    callingFeatureId, launchIntent, opts, user.getIdentifier());
         }
 
         @Override
         public void showAppDetailsAsUser(IApplicationThread caller,
-                String callingPackage, ComponentName component,
+                String callingPackage, String callingFeatureId, ComponentName component,
                 Rect sourceBounds, Bundle opts, UserHandle user) throws RemoteException {
             if (!canAccessProfile(user.getIdentifier(), "Cannot show app details")) {
                 return;
@@ -975,7 +978,7 @@
                 Binder.restoreCallingIdentity(ident);
             }
             mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
-                    intent, opts, user.getIdentifier());
+                    callingFeatureId, intent, opts, user.getIdentifier());
         }
 
         /** Checks if user is a profile of or same as listeningUser.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 88c048c..2c85d06 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6298,10 +6298,11 @@
 
     private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
             Intent origIntent, String resolvedType, String callingPackage,
-            boolean isRequesterInstantApp, Bundle verificationBundle, int userId) {
+            @Nullable String callingFeatureId, boolean isRequesterInstantApp,
+            Bundle verificationBundle, int userId) {
         final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO,
                 new InstantAppRequest(responseObj, origIntent, resolvedType,
-                        callingPackage, isRequesterInstantApp, userId, verificationBundle,
+                        callingPackage, callingFeatureId, isRequesterInstantApp, userId, verificationBundle,
                         false /*resolveForStart*/, responseObj.hostDigestPrefixSecure,
                         responseObj.token));
         mHandler.sendMessage(msg);
@@ -6987,10 +6988,10 @@
                 Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
                 String token = UUID.randomUUID().toString();
                 InstantAppDigest digest = InstantAppResolver.parseDigest(intent);
-                final InstantAppRequest requestObject = new InstantAppRequest(
-                        null /*responseObj*/, intent /*origIntent*/, resolvedType,
-                        null /*callingPackage*/, isRequesterInstantApp,
-                        userId, null /*verificationBundle*/, resolveForStart,
+                final InstantAppRequest requestObject = new InstantAppRequest(null /*responseObj*/,
+                        intent /*origIntent*/, resolvedType, null /*callingPackage*/,
+                        null /*callingFeatureId*/, isRequesterInstantApp, userId,
+                        null /*verificationBundle*/, resolveForStart,
                         digest.getDigestPrefixSecure(), token);
                 auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(
                         mInstantAppResolverConnection, requestObject);
@@ -12206,7 +12207,7 @@
                         + intent.toShortString(false, true, false, false)
                         + " " + intent.getExtras(), here);
             }
-            am.broadcastIntent(null, intent, null, finishedReceiver,
+            am.broadcastIntentWithFeature(null, null, intent, null, finishedReceiver,
                     0, null, null, requiredPermissions, android.app.AppOpsManager.OP_NONE,
                     null, finishedReceiver != null, false, id);
         }
@@ -12368,8 +12369,9 @@
                 lockedBcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
             }
             final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED};
-            am.broadcastIntent(null, lockedBcIntent, null, null, 0, null, null, requiredPermissions,
-                    android.app.AppOpsManager.OP_NONE, null, false, false, userId);
+            am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null,
+                    requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
+                    userId);
 
             // Deliver BOOT_COMPLETED only if user is unlocked
             if (mUserManager.isUserUnlockingOrUnlocked(userId)) {
@@ -12377,8 +12379,9 @@
                 if (includeStopped) {
                     bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
                 }
-                am.broadcastIntent(null, bcIntent, null, null, 0, null, null, requiredPermissions,
-                        android.app.AppOpsManager.OP_NONE, null, false, false, userId);
+                am.broadcastIntentWithFeature(null, null, bcIntent, null, null, 0, null, null,
+                        requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
+                        userId);
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -18781,7 +18784,7 @@
             intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             try {
-                am.broadcastIntent(null, intent, null, null,
+                am.broadcastIntentWithFeature(null, null, intent, null, null,
                         0, null, null, null, android.app.AppOpsManager.OP_NONE,
                         null, false, false, userId);
             } catch (RemoteException e) {
@@ -23425,9 +23428,10 @@
         @Override
         public void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
                 Intent origIntent, String resolvedType, String callingPackage,
-                boolean isRequesterInstantApp, Bundle verificationBundle, int userId) {
-            PackageManagerService.this.requestInstantAppResolutionPhaseTwo(
-                    responseObj, origIntent, resolvedType, callingPackage, isRequesterInstantApp,
+                @Nullable String callingFeatureId, boolean isRequesterInstantApp,
+                Bundle verificationBundle, int userId) {
+            PackageManagerService.this.requestInstantAppResolutionPhaseTwo(responseObj, origIntent,
+                    resolvedType, callingPackage, callingFeatureId, isRequesterInstantApp,
                     verificationBundle, userId);
         }
 
@@ -24337,8 +24341,9 @@
                 Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY,
             };
             try {
-                am.broadcastIntent(null, intent, null, null, 0, null, null, requiredPermissions,
-                        android.app.AppOpsManager.OP_NONE, null, false, false, UserHandle.USER_ALL);
+                am.broadcastIntentWithFeature(null, null, intent, null, null, 0, null, null,
+                        requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
+                        UserHandle.USER_ALL);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index b9bb9e0..bbd319cb 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -182,7 +182,10 @@
     public void updateProcesses() {
         processes = null;
         for (int i = packages.size() - 1; i >= 0; i--) {
-            addProcesses(packages.valueAt(i).pkg.getProcesses());
+            final AndroidPackage pkg = packages.valueAt(i).pkg;
+            if (pkg != null) {
+                addProcesses(pkg.getProcesses());
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 6e4ec7e..cb755f9 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -35,13 +35,16 @@
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyEventLogger;
+import android.app.admin.DevicePolicyManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.IntentSender;
+import android.content.pm.CrossProfileAppsInternal;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.UserInfo;
 import android.content.pm.UserInfo.UserInfoFlag;
@@ -258,6 +261,10 @@
     /** Installs system packages based on user-type. */
     private final UserSystemPackageInstaller mSystemPackageInstaller;
 
+    private PackageManagerInternal mPmInternal;
+    private CrossProfileAppsInternal mCrossProfileAppsInternal;
+    private DevicePolicyManagerInternal mDevicePolicyManagerInternal;
+
     /**
      * Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps.
      */
@@ -944,6 +951,8 @@
         intent.putExtra(Intent.EXTRA_QUIET_MODE, inQuietMode);
         intent.putExtra(Intent.EXTRA_USER, profileHandle);
         intent.putExtra(Intent.EXTRA_USER_HANDLE, profileHandle.getIdentifier());
+        getDevicePolicyManagerInternal().broadcastIntentToCrossProfileManifestReceiversAsUser(
+                intent, parentHandle, /* requiresPermission= */ true);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
         mContext.sendBroadcastAsUser(intent, parentHandle);
     }
@@ -3845,11 +3854,15 @@
 
     private void sendProfileRemovedBroadcast(int parentUserId, int removedUserId) {
         Intent managedProfileIntent = new Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED);
-        managedProfileIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
-                Intent.FLAG_RECEIVER_FOREGROUND);
         managedProfileIntent.putExtra(Intent.EXTRA_USER, new UserHandle(removedUserId));
         managedProfileIntent.putExtra(Intent.EXTRA_USER_HANDLE, removedUserId);
-        mContext.sendBroadcastAsUser(managedProfileIntent, new UserHandle(parentUserId), null);
+        final UserHandle parentHandle = new UserHandle(parentUserId);
+        getDevicePolicyManagerInternal().broadcastIntentToCrossProfileManifestReceiversAsUser(
+                managedProfileIntent, parentHandle, /* requiresPermission= */ false);
+        managedProfileIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                | Intent.FLAG_RECEIVER_FOREGROUND);
+        mContext.sendBroadcastAsUser(managedProfileIntent, parentHandle,
+                /* receiverPermission= */null);
     }
 
     @Override
@@ -5095,7 +5108,7 @@
     }
 
     /**
-     * Check if the calling package name matches with the calling UID, throw
+     * Checks if the calling package name matches with the calling UID, throw
      * {@link SecurityException} if not.
      */
     private void verifyCallingPackage(String callingPackage, int callingUid) {
@@ -5105,4 +5118,30 @@
                     + " does not match the calling uid " + callingUid);
         }
     }
+
+    /** Retrieves the internal package manager interface. */
+    private PackageManagerInternal getPackageManagerInternal() {
+        // Don't need to synchonize; worst-case scenario LocalServices will be called twice.
+        if (mPmInternal == null) {
+            mPmInternal = LocalServices.getService(PackageManagerInternal.class);
+        }
+        return mPmInternal;
+    }
+
+    /** Retrieve the internal cross profile apps interface. */
+    private CrossProfileAppsInternal getCrossProfileAppsInternal() {
+        if (mCrossProfileAppsInternal == null) {
+            mCrossProfileAppsInternal = LocalServices.getService(CrossProfileAppsInternal.class);
+        }
+        return mCrossProfileAppsInternal;
+    }
+
+    /** Returns the internal device policy manager interface. */
+    private DevicePolicyManagerInternal getDevicePolicyManagerInternal() {
+        if (mDevicePolicyManagerInternal == null) {
+            mDevicePolicyManagerInternal =
+                    LocalServices.getService(DevicePolicyManagerInternal.class);
+        }
+        return mDevicePolicyManagerInternal;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
index b809951..976ce1f 100644
--- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -36,6 +36,8 @@
 
     private static final String LOG_TAG = OneTimePermissionUserManager.class.getSimpleName();
 
+    private static final boolean DEBUG = true;
+
     private final @NonNull Context mContext;
     private final @NonNull ActivityManager mActivityManager;
     private final @NonNull AlarmManager mAlarmManager;
@@ -156,6 +158,14 @@
 
         private PackageInactivityListener(int uid, @NonNull String packageName, long timeout,
                 int importanceToResetTimer, int importanceToKeepSessionAlive) {
+
+            if (DEBUG) {
+                Log.d(LOG_TAG,
+                        "Start tracking " + packageName + ". uid=" + uid + " timeout=" + timeout
+                                + " importanceToResetTimer=" + importanceToResetTimer
+                                + " importanceToKeepSessionAlive=" + importanceToKeepSessionAlive);
+            }
+
             mUid = uid;
             mPackageName = packageName;
             mTimeout = timeout;
@@ -182,6 +192,12 @@
             if (uid != mUid) {
                 return;
             }
+
+
+            if (DEBUG) {
+                Log.d(LOG_TAG, "Importance changed for " + mPackageName + " (" + mUid + ")."
+                        + " importance=" + importance);
+            }
             synchronized (mInnerLock) {
                 if (importance > IMPORTANCE_CACHED) {
                     onPackageInactiveLocked();
@@ -257,8 +273,15 @@
             mIsFinished = true;
             cancelAlarmLocked();
             mContext.getMainThreadHandler().post(
-                    () -> mPermissionControllerManager.notifyOneTimePermissionSessionTimeout(
-                            mPackageName));
+                    () -> {
+                        if (DEBUG) {
+                            Log.d(LOG_TAG, "One time session expired for "
+                                    + mPackageName + " (" + mUid + ").");
+                        }
+
+                        mPermissionControllerManager.notifyOneTimePermissionSessionTimeout(
+                                mPackageName);
+                    });
             mActivityManager.removeOnUidImportanceListener(mStartTimerListener);
             mActivityManager.removeOnUidImportanceListener(mSessionKillableListener);
             mActivityManager.removeOnUidImportanceListener(mGoneListener);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6126787..a45a996 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5197,7 +5197,8 @@
                     final Intent dock = createHomeDockIntent();
                     if (dock != null) {
                         int result = ActivityTaskManager.getService()
-                                .startActivityAsUser(null, mContext.getBasePackageName(), dock,
+                                .startActivityAsUser(null, mContext.getBasePackageName(),
+                                        mContext.getFeatureId(), dock,
                                         dock.resolveTypeIfNeeded(mContext.getContentResolver()),
                                         null, null, 0,
                                         ActivityManager.START_FLAG_ONLY_IF_NEEDED,
@@ -5208,7 +5209,8 @@
                     }
                 }
                 int result = ActivityTaskManager.getService()
-                        .startActivityAsUser(null, mContext.getBasePackageName(), mHomeIntent,
+                        .startActivityAsUser(null, mContext.getBasePackageName(),
+                                mContext.getFeatureId(), mHomeIntent,
                                 mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
                                 null, null, 0,
                                 ActivityManager.START_FLAG_ONLY_IF_NEEDED,
diff --git a/services/core/java/com/android/server/policy/TEST_MAPPING b/services/core/java/com/android/server/policy/TEST_MAPPING
index 484017b..094e70f 100644
--- a/services/core/java/com/android/server/policy/TEST_MAPPING
+++ b/services/core/java/com/android/server/policy/TEST_MAPPING
@@ -36,6 +36,9 @@
         },
         {
           "include-filter": "android.permission2.cts.RestrictedStoragePermissionSharedUidTest"
+        },
+        {
+          "include-filter": "android.permission2.cts.RestrictedStoragePermissionTest"
         }
       ]
     },
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index e81214e..67a22d3 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -75,7 +75,6 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -83,7 +82,6 @@
 import android.view.IApplicationToken;
 import android.view.IDisplayFoldListener;
 import android.view.IWindowManager;
-import android.view.InputEventReceiver;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
@@ -471,6 +469,10 @@
          * Remove the input consumer from the window manager.
          */
         void dismiss();
+        /**
+         * Dispose the input consumer and input receiver from UI thread.
+         */
+        void dispose();
     }
 
     /**
@@ -505,12 +507,6 @@
         public static final int CAMERA_LENS_COVERED = 1;
 
         /**
-         * Add a input consumer which will consume all input events going to any window below it.
-         */
-        public InputConsumer createInputConsumer(Looper looper, String name,
-                InputEventReceiver.Factory inputEventReceiverFactory, int displayId);
-
-        /**
          * Returns a code that describes the current state of the lid switch.
          */
         public int getLidState();
diff --git a/services/core/java/com/android/server/power/AttentionDetector.java b/services/core/java/com/android/server/power/AttentionDetector.java
index cc72dd6..1ab6ade 100644
--- a/services/core/java/com/android/server/power/AttentionDetector.java
+++ b/services/core/java/com/android/server/power/AttentionDetector.java
@@ -17,7 +17,7 @@
 package com.android.server.power;
 
 import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
-import static android.provider.Settings.System.ADAPTIVE_SLEEP;
+import static android.provider.Settings.Secure.ADAPTIVE_SLEEP;
 
 import android.Manifest;
 import android.app.ActivityManager;
@@ -152,8 +152,8 @@
 
     @VisibleForTesting
     void updateEnabledFromSettings(Context context) {
-        mIsSettingEnabled = Settings.System.getIntForUser(context.getContentResolver(),
-                Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1;
+        mIsSettingEnabled = Settings.Secure.getIntForUser(context.getContentResolver(),
+                Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1;
     }
 
     public void systemReady(Context context) {
@@ -173,8 +173,8 @@
             // Shouldn't happen since in-process.
         }
 
-        context.getContentResolver().registerContentObserver(Settings.System.getUriFor(
-                Settings.System.ADAPTIVE_SLEEP),
+        context.getContentResolver().registerContentObserver(Settings.Secure.getUriFor(
+                Settings.Secure.ADAPTIVE_SLEEP),
                 false, new ContentObserver(new Handler(context.getMainLooper())) {
                     @Override
                     public void onChange(boolean selfChange) {
@@ -194,7 +194,7 @@
         if (!isAttentionServiceSupported() || !serviceHasSufficientPermissions()) {
             // Turns off adaptive sleep in settings for all users if attention service is not
             // available. The setting itself should also be grayed out in this case.
-            Settings.System.putInt(mContentResolver, ADAPTIVE_SLEEP, 0);
+            Settings.Secure.putInt(mContentResolver, ADAPTIVE_SLEEP, 0);
             return nextScreenDimming;
         }
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index aa6d854..e26dac6 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -362,6 +362,7 @@
     private static final String TAG_PERSISTABLEBUNDLE = "persistable_bundle";
     private static final String ATTR_LAUNCHEDFROMUID = "launched_from_uid";
     private static final String ATTR_LAUNCHEDFROMPACKAGE = "launched_from_package";
+    private static final String ATTR_LAUNCHEDFROMFEATURE = "launched_from_feature";
     private static final String ATTR_RESOLVEDTYPE = "resolved_type";
     private static final String ATTR_COMPONENTSPECIFIED = "component_specified";
     static final String ACTIVITY_ICON_SUFFIX = "_activity_icon_";
@@ -422,6 +423,7 @@
     final int launchedFromPid; // always the pid who started the activity.
     final int launchedFromUid; // always the uid who started the activity.
     final String launchedFromPackage; // always the package who started the activity.
+    final @Nullable String launchedFromFeatureId; // always the feature in launchedFromPackage
     final Intent intent;    // the original intent that generated us
     final String shortComponentName; // the short component name of the intent
     final String resolvedType; // as per original caller;
@@ -773,6 +775,7 @@
                 pw.print(" processName="); pw.println(processName);
         pw.print(prefix); pw.print("launchedFromUid="); pw.print(launchedFromUid);
                 pw.print(" launchedFromPackage="); pw.print(launchedFromPackage);
+                pw.print(" launchedFromFeature="); pw.print(launchedFromFeatureId);
                 pw.print(" userId="); pw.println(mUserId);
         pw.print(prefix); pw.print("app="); pw.println(app);
         pw.print(prefix); pw.println(intent.toInsecureString());
@@ -1483,9 +1486,10 @@
     }
 
     ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
-            int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage, Intent _intent,
-            String _resolvedType, ActivityInfo aInfo, Configuration _configuration,
-            ActivityRecord _resultTo, String _resultWho, int _reqCode, boolean _componentSpecified,
+            int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage,
+            @Nullable String _launchedFromFeature, Intent _intent, String _resolvedType,
+            ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo,
+            String _resultWho, int _reqCode, boolean _componentSpecified,
             boolean _rootVoiceInteraction, ActivityStackSupervisor supervisor,
             ActivityOptions options, ActivityRecord sourceRecord) {
         super(_service.mWindowManager, new Token(_intent).asBinder(), TYPE_APPLICATION, true,
@@ -1564,6 +1568,7 @@
         launchedFromPid = _launchedFromPid;
         launchedFromUid = _launchedFromUid;
         launchedFromPackage = _launchedFromPackage;
+        launchedFromFeatureId = _launchedFromFeature;
         shortComponentName = _intent.getComponent().flattenToShortString();
         resolvedType = _resolvedType;
         componentSpecified = _componentSpecified;
@@ -2284,7 +2289,7 @@
      * @return Whether AppOps allows this package to enter picture-in-picture.
      */
     private boolean checkEnterPictureInPictureAppOpsState() {
-        return mAtmService.getAppOpsService().checkOperation(
+        return mAtmService.getAppOpsManager().checkOpNoThrow(
                 OP_PICTURE_IN_PICTURE, info.applicationInfo.uid, packageName) == MODE_ALLOWED;
     }
 
@@ -7282,6 +7287,9 @@
         if (launchedFromPackage != null) {
             out.attribute(null, ATTR_LAUNCHEDFROMPACKAGE, launchedFromPackage);
         }
+        if (launchedFromFeatureId != null) {
+            out.attribute(null, ATTR_LAUNCHEDFROMFEATURE, launchedFromFeatureId);
+        }
         if (resolvedType != null) {
             out.attribute(null, ATTR_RESOLVEDTYPE, resolvedType);
         }
@@ -7309,6 +7317,7 @@
         PersistableBundle persistentState = null;
         int launchedFromUid = 0;
         String launchedFromPackage = null;
+        String launchedFromFeature = null;
         String resolvedType = null;
         boolean componentSpecified = false;
         int userId = 0;
@@ -7327,6 +7336,8 @@
                 launchedFromUid = Integer.parseInt(attrValue);
             } else if (ATTR_LAUNCHEDFROMPACKAGE.equals(attrName)) {
                 launchedFromPackage = attrValue;
+            } else if (ATTR_LAUNCHEDFROMFEATURE.equals(attrName)) {
+                launchedFromFeature = attrValue;
             } else if (ATTR_RESOLVEDTYPE.equals(attrName)) {
                 resolvedType = attrValue;
             } else if (ATTR_COMPONENTSPECIFIED.equals(attrName)) {
@@ -7374,10 +7385,11 @@
                     " resolvedType=" + resolvedType);
         }
         final ActivityRecord r = new ActivityRecord(service, null /* caller */,
-                0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, intent, resolvedType,
-                aInfo, service.getConfiguration(), null /* resultTo */, null /* resultWho */,
-                0 /* reqCode */, componentSpecified, false /* rootVoiceInteraction */,
-                stackSupervisor, null /* options */, null /* sourceRecord */);
+                0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, launchedFromFeature,
+                intent, resolvedType, aInfo, service.getConfiguration(), null /* resultTo */,
+                null /* resultWho */, 0 /* reqCode */, componentSpecified,
+                false /* rootVoiceInteraction */, stackSupervisor, null /* options */,
+                null /* sourceRecord */);
 
         r.mPersistentState = persistentState;
         r.taskDescription = taskDescription;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index d380f8c..ab8e975 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -60,18 +60,6 @@
 import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
 
-import static com.android.server.wm.TaskProto.ACTIVITIES;
-import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
-import static com.android.server.wm.TaskProto.BOUNDS;
-import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
-import static com.android.server.wm.TaskProto.DISPLAY_ID;
-import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
-import static com.android.server.wm.TaskProto.MIN_HEIGHT;
-import static com.android.server.wm.TaskProto.MIN_WIDTH;
-import static com.android.server.wm.TaskProto.ORIG_ACTIVITY;
-import static com.android.server.wm.TaskProto.REAL_ACTIVITY;
-import static com.android.server.wm.TaskProto.RESIZE_MODE;
-import static com.android.server.wm.TaskProto.RESUMED_ACTIVITY;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
 import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
@@ -115,14 +103,26 @@
 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
 import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
 import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.TaskProto.ACTIVITIES;
+import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
 import static com.android.server.wm.TaskProto.ADJUSTED_BOUNDS;
 import static com.android.server.wm.TaskProto.ADJUSTED_FOR_IME;
 import static com.android.server.wm.TaskProto.ADJUST_DIVIDER_AMOUNT;
 import static com.android.server.wm.TaskProto.ADJUST_IME_AMOUNT;
 import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS;
+import static com.android.server.wm.TaskProto.BOUNDS;
 import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
+import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
+import static com.android.server.wm.TaskProto.DISPLAY_ID;
 import static com.android.server.wm.TaskProto.FILLS_PARENT;
+import static com.android.server.wm.TaskProto.LAST_NON_FULLSCREEN_BOUNDS;
 import static com.android.server.wm.TaskProto.MINIMIZE_AMOUNT;
+import static com.android.server.wm.TaskProto.MIN_HEIGHT;
+import static com.android.server.wm.TaskProto.MIN_WIDTH;
+import static com.android.server.wm.TaskProto.ORIG_ACTIVITY;
+import static com.android.server.wm.TaskProto.REAL_ACTIVITY;
+import static com.android.server.wm.TaskProto.RESIZE_MODE;
+import static com.android.server.wm.TaskProto.RESUMED_ACTIVITY;
 import static com.android.server.wm.TaskProto.ROOT_TASK_ID;
 import static com.android.server.wm.TaskProto.SURFACE_HEIGHT;
 import static com.android.server.wm.TaskProto.SURFACE_WIDTH;
@@ -135,6 +135,7 @@
 import static java.lang.Integer.MAX_VALUE;
 
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -622,7 +623,7 @@
                 true /*neverRelinquishIdentity*/,
                 _taskDescription != null ? _taskDescription : new ActivityManager.TaskDescription(),
                 id, INVALID_TASK_ID, INVALID_TASK_ID, 0 /*taskAffiliationColor*/,
-                info.applicationInfo.uid, info.packageName, info.resizeMode,
+                info.applicationInfo.uid, info.packageName, null, info.resizeMode,
                 info.supportsPictureInPicture(), false /*_realActivitySuspended*/,
                 false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE, info,
                 _voiceSession, _voiceInteractor, stack);
@@ -635,15 +636,16 @@
             String _lastDescription, long lastTimeMoved, boolean neverRelinquishIdentity,
             ActivityManager.TaskDescription _lastTaskDescription, int taskAffiliation,
             int prevTaskId, int nextTaskId, int taskAffiliationColor, int callingUid,
-            String callingPackage, int resizeMode, boolean supportsPictureInPicture,
-            boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight,
+            String callingPackage, @Nullable String callingFeatureId, int resizeMode,
+            boolean supportsPictureInPicture, boolean _realActivitySuspended,
+            boolean userSetupComplete, int minWidth, int minHeight,
             ActivityInfo info, IVoiceInteractionSession _voiceSession,
             IVoiceInteractor _voiceInteractor, ActivityStack stack) {
         super(atmService, id, _intent, _affinityIntent, _affinity, _rootAffinity,
                 _realActivity, _origActivity, _rootWasReset, _autoRemoveRecents, _askedCompatMode,
                 _userId, _effectiveUid, _lastDescription, lastTimeMoved, neverRelinquishIdentity,
                 _lastTaskDescription, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor,
-                callingUid, callingPackage, resizeMode, supportsPictureInPicture,
+                callingUid, callingPackage, callingFeatureId, resizeMode, supportsPictureInPicture,
                 _realActivitySuspended, userSetupComplete, minWidth, minHeight, info, _voiceSession,
                 _voiceInteractor, stack);
 
@@ -826,6 +828,17 @@
         setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
                 false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
                 false /* creating */);
+        if (windowingMode == WINDOWING_MODE_PINNED) {
+            // This stack will be visible before SystemUI requests PiP animation to start, and if
+            // the selected animation type is fade in, this stack will be close first. If the
+            // animation does not start early, user may see this full screen window appear again
+            // after the closing transition finish, which could cause screen to flicker.
+            // Check if animation should start here in advance.
+            final BoundsAnimationController controller = getDisplay().mBoundsAnimationController;
+            if (controller.isAnimationTypeFadeIn()) {
+                setPinnedStackAlpha(0f);
+            }
+        }
     }
 
     /**
@@ -2924,6 +2937,7 @@
                             .setCallingPid(-1)
                             .setCallingUid(parent.launchedFromUid)
                             .setCallingPackage(parent.launchedFromPackage)
+                            .setCallingFeatureId(parent.launchedFromFeatureId)
                             .setRealCallingPid(-1)
                             .setRealCallingUid(parent.launchedFromUid)
                             .setComponentSpecified(true)
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a513ef8..a582f21 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -88,6 +88,7 @@
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -260,7 +261,9 @@
     /** Short cut */
     private WindowManagerService mWindowManager;
 
-     /** Common synchronization logic used to save things to disks. */
+    private AppOpsManager mAppOpsManager;
+
+    /** Common synchronization logic used to save things to disks. */
     PersisterQueue mPersisterQueue;
     LaunchParamsPersister mLaunchParamsPersister;
     private LaunchParamsController mLaunchParamsController;
@@ -1047,8 +1050,8 @@
 
     boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
             int requestCode, int callingPid, int callingUid, String callingPackage,
-            boolean ignoreTargetSecurity, boolean launchingInTask,
-            WindowProcessController callerApp, ActivityRecord resultRecord,
+            @Nullable String callingFeatureId, boolean ignoreTargetSecurity,
+            boolean launchingInTask, WindowProcessController callerApp, ActivityRecord resultRecord,
             ActivityStack resultStack) {
         final boolean isCallerRecents = mService.getRecentTasks() != null
                 && mService.getRecentTasks().isCallerRecents(callingUid);
@@ -1060,10 +1063,10 @@
             // existing task, then also allow the activity to be fully relaunched.
             return true;
         }
-        final int componentRestriction = getComponentRestrictionForCallingPackage(
-                aInfo, callingPackage, callingPid, callingUid, ignoreTargetSecurity);
+        final int componentRestriction = getComponentRestrictionForCallingPackage(aInfo,
+                callingPackage, callingFeatureId, callingPid, callingUid, ignoreTargetSecurity);
         final int actionRestriction = getActionRestrictionForCallingPackage(
-                intent.getAction(), callingPackage, callingPid, callingUid);
+                intent.getAction(), callingPackage, callingFeatureId, callingPid, callingUid);
         if (componentRestriction == ACTIVITY_RESTRICTION_PERMISSION
                 || actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
             if (resultRecord != null) {
@@ -1194,8 +1197,16 @@
         }
     }
 
+    private AppOpsManager getAppOpsManager() {
+        if (mAppOpsManager == null) {
+            mAppOpsManager = mService.mContext.getSystemService(AppOpsManager.class);
+        }
+        return mAppOpsManager;
+    }
+
     private int getComponentRestrictionForCallingPackage(ActivityInfo activityInfo,
-            String callingPackage, int callingPid, int callingUid, boolean ignoreTargetSecurity) {
+            String callingPackage, @Nullable String callingFeatureId, int callingPid,
+            int callingUid, boolean ignoreTargetSecurity) {
         if (!ignoreTargetSecurity && mService.checkComponentPermission(activityInfo.permission,
                 callingPid, callingUid, activityInfo.applicationInfo.uid, activityInfo.exported)
                 == PERMISSION_DENIED) {
@@ -1211,9 +1222,8 @@
             return ACTIVITY_RESTRICTION_NONE;
         }
 
-        // TODO moltmann b/136595429: Set featureId from caller
-        if (mService.getAppOpsService().noteOperation(opCode, callingUid,
-                callingPackage, /* featureId */ null, false, "") != AppOpsManager.MODE_ALLOWED) {
+        if (getAppOpsManager().noteOpNoThrow(opCode, callingUid,
+                callingPackage, callingFeatureId, "") != AppOpsManager.MODE_ALLOWED) {
             if (!ignoreTargetSecurity) {
                 return ACTIVITY_RESTRICTION_APPOP;
             }
@@ -1222,8 +1232,8 @@
         return ACTIVITY_RESTRICTION_NONE;
     }
 
-    private int getActionRestrictionForCallingPackage(String action,
-            String callingPackage, int callingPid, int callingUid) {
+    private int getActionRestrictionForCallingPackage(String action, String callingPackage,
+            @Nullable String callingFeatureId, int callingPid, int callingUid) {
         if (action == null) {
             return ACTIVITY_RESTRICTION_NONE;
         }
@@ -1256,9 +1266,8 @@
             return ACTIVITY_RESTRICTION_NONE;
         }
 
-        // TODO moltmann b/136595429: Set featureId from caller
-        if (mService.getAppOpsService().noteOperation(opCode, callingUid,
-                callingPackage, /* featureId */ null, false, "") != AppOpsManager.MODE_ALLOWED) {
+        if (getAppOpsManager().noteOpNoThrow(opCode, callingUid,
+                callingPackage, callingFeatureId, "") != AppOpsManager.MODE_ALLOWED) {
             return ACTIVITY_RESTRICTION_APPOP;
         }
 
@@ -2701,6 +2710,7 @@
             SafeActivityOptions options) {
         Task task = null;
         final String callingPackage;
+        final String callingFeatureId;
         final Intent intent;
         final int userId;
         int activityType = ACTIVITY_TYPE_UNDEFINED;
@@ -2780,11 +2790,12 @@
                 return ActivityManager.START_TASK_TO_FRONT;
             }
             callingPackage = task.mCallingPackage;
+            callingFeatureId = task.mCallingFeatureId;
             intent = task.intent;
             intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
             userId = task.mUserId;
-            return mService.getActivityStartController().startActivityInPackage(
-                    task.mCallingUid, callingPid, callingUid, callingPackage, intent, null, null,
+            return mService.getActivityStartController().startActivityInPackage(task.mCallingUid,
+                    callingPid, callingUid, callingPackage, callingFeatureId, intent, null, null,
                     null, 0, 0, options, userId, task, "startActivityFromRecents",
                     false /* validateIncomingUser */, null /* originatingPendingIntent */,
                     false /* allowBackgroundActivityStart */);
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index f35ba9e..881dc81 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -26,6 +26,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
+import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
 import android.content.ComponentName;
@@ -278,10 +279,11 @@
     }
 
     final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
-            String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
-            String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
-            int userId, Task inTask, String reason, boolean validateIncomingUser,
-            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
+            String callingPackage, @Nullable String callingFeatureId, Intent intent,
+            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
+            boolean allowBackgroundActivityStart) {
 
         userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
                 reason);
@@ -292,6 +294,7 @@
                 .setRealCallingPid(realCallingPid)
                 .setRealCallingUid(realCallingUid)
                 .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setResultTo(resultTo)
                 .setResultWho(resultWho)
@@ -310,19 +313,20 @@
      *
      * @param uid Make a call as if this UID did.
      * @param callingPackage Make a call as if this package did.
+     * @param callingFeatureId Make a call as if this feature in the package did.
      * @param intents Intents to start.
      * @param userId Start the intents on this user.
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
      * @param originatingPendingIntent PendingIntentRecord that originated this activity start or
      *        null if not originated by PendingIntent
      */
-    final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
-            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
-            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
-            boolean allowBackgroundActivityStart) {
+    final int startActivitiesInPackage(int uid, String callingPackage,
+            @Nullable String callingFeatureId, Intent[] intents, String[] resolvedTypes,
+            IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser,
+            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
         return startActivitiesInPackage(uid, 0 /* realCallingPid */, -1 /* realCallingUid */,
-            callingPackage, intents, resolvedTypes, resultTo, options, userId, validateIncomingUser,
-            originatingPendingIntent, allowBackgroundActivityStart);
+                callingPackage, callingFeatureId, intents, resolvedTypes, resultTo, options, userId,
+                validateIncomingUser, originatingPendingIntent, allowBackgroundActivityStart);
     }
 
     /**
@@ -339,9 +343,9 @@
      *        null if not originated by PendingIntent
      */
     final int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
-            String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo,
-            SafeActivityOptions options, int userId, boolean validateIncomingUser,
-            PendingIntentRecord originatingPendingIntent,
+            String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
+            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
             boolean allowBackgroundActivityStart) {
 
         final String reason = "startActivityInPackage";
@@ -350,14 +354,14 @@
                 Binder.getCallingUid(), reason);
 
         // TODO: Switch to user app stacks here.
-        return startActivities(null, uid, realCallingPid, realCallingUid, callingPackage, intents,
-                resolvedTypes, resultTo, options, userId, reason, originatingPendingIntent,
-                allowBackgroundActivityStart);
+        return startActivities(null, uid, realCallingPid, realCallingUid, callingPackage,
+                callingFeatureId, intents, resolvedTypes, resultTo, options, userId, reason,
+                originatingPendingIntent, allowBackgroundActivityStart);
     }
 
     int startActivities(IApplicationThread caller, int callingUid, int incomingRealCallingPid,
-            int incomingRealCallingUid, String callingPackage, Intent[] intents,
-            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
+            int incomingRealCallingUid, String callingPackage, @Nullable String callingFeatureId,
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
             int userId, String reason, PendingIntentRecord originatingPendingIntent,
             boolean allowBackgroundActivityStart) {
         if (intents == null) {
@@ -435,6 +439,7 @@
                         .setCallingPid(callingPid)
                         .setCallingUid(callingUid)
                         .setCallingPackage(callingPackage)
+                        .setCallingFeatureId(callingFeatureId)
                         .setRealCallingPid(realCallingPid)
                         .setRealCallingUid(realCallingUid)
                         .setActivityOptions(checkedOptions)
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 76aa1d1..1009771 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -34,6 +34,7 @@
 
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 
+import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManagerInternal;
@@ -85,6 +86,7 @@
     private int mUserId;
     private int mStartFlags;
     private String mCallingPackage;
+    private @Nullable String mCallingFeatureId;
 
     /*
      * Per-intent states that were load from ActivityStarter and are subject to modifications
@@ -120,19 +122,20 @@
      * method should not be changed during intercept.
      */
     void setStates(int userId, int realCallingPid, int realCallingUid, int startFlags,
-            String callingPackage) {
+            String callingPackage, @Nullable String callingFeatureId) {
         mRealCallingPid = realCallingPid;
         mRealCallingUid = realCallingUid;
         mUserId = userId;
         mStartFlags = startFlags;
         mCallingPackage = callingPackage;
+        mCallingFeatureId = callingFeatureId;
     }
 
     private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) {
         Bundle activityOptions = deferCrossProfileAppsAnimationIfNecessary();
         final IIntentSender target = mService.getIntentSenderLocked(
-                INTENT_SENDER_ACTIVITY, mCallingPackage, callingUid, mUserId, null /*token*/,
-                null /*resultCode*/, 0 /*requestCode*/,
+                INTENT_SENDER_ACTIVITY, mCallingPackage, mCallingFeatureId, callingUid, mUserId,
+                null /*token*/, null /*resultCode*/, 0 /*requestCode*/,
                 new Intent[] { mIntent }, new String[] { mResolvedType },
                 flags, activityOptions);
         return new IntentSender(target);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c7270f2..8ed798c 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -318,6 +318,7 @@
         int callingPid = DEFAULT_CALLING_PID;
         int callingUid = DEFAULT_CALLING_UID;
         String callingPackage;
+        @Nullable String callingFeatureId;
         int realCallingPid = DEFAULT_REAL_CALLING_PID;
         int realCallingUid = DEFAULT_REAL_CALLING_UID;
         int startFlags;
@@ -367,6 +368,7 @@
             callingPid = DEFAULT_CALLING_PID;
             callingUid = DEFAULT_CALLING_UID;
             callingPackage = null;
+            callingFeatureId = null;
             realCallingPid = DEFAULT_REAL_CALLING_PID;
             realCallingUid = DEFAULT_REAL_CALLING_UID;
             startFlags = 0;
@@ -405,6 +407,7 @@
             callingPid = request.callingPid;
             callingUid = request.callingUid;
             callingPackage = request.callingPackage;
+            callingFeatureId = request.callingFeatureId;
             realCallingPid = request.realCallingPid;
             realCallingUid = request.realCallingUid;
             startFlags = request.startFlags;
@@ -693,9 +696,10 @@
         }
 
         final IIntentSender target = mService.getIntentSenderLocked(
-                ActivityManager.INTENT_SENDER_ACTIVITY, "android" /* packageName */, appCallingUid,
-                mRequest.userId, null /* token */, null /* resultWho*/, 0 /* requestCode*/,
-                new Intent[] { mRequest.intent }, new String[] { mRequest.resolvedType },
+                ActivityManager.INTENT_SENDER_ACTIVITY, "android" /* packageName */,
+                null /* featureId */, appCallingUid, mRequest.userId, null /* token */,
+                null /* resultWho*/, 0 /* requestCode*/, new Intent[]{mRequest.intent},
+                new String[]{mRequest.resolvedType},
                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT,
                 null /* bOptions */);
 
@@ -807,6 +811,7 @@
         int callingPid = request.callingPid;
         int callingUid = request.callingUid;
         String callingPackage = request.callingPackage;
+        String callingFeatureId = request.callingFeatureId;
         final int realCallingPid = request.realCallingPid;
         final int realCallingUid = request.realCallingUid;
         final int startFlags = request.startFlags;
@@ -882,6 +887,7 @@
                 // we want the final activity to consider it to have been launched by the
                 // previous app activity.
                 callingPackage = sourceRecord.launchedFromPackage;
+                callingFeatureId = sourceRecord.launchedFromFeatureId;
             }
         }
 
@@ -949,8 +955,8 @@
         }
 
         boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
-                requestCode, callingPid, callingUid, callingPackage, request.ignoreTargetSecurity,
-                inTask != null, callerApp, resultRecord, resultStack);
+                requestCode, callingPid, callingUid, callingPackage, callingFeatureId,
+                request.ignoreTargetSecurity, inTask != null, callerApp, resultRecord, resultStack);
         abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                 callingPid, resolvedType, aInfo.applicationInfo);
         abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,
@@ -990,7 +996,8 @@
             }
         }
 
-        mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage);
+        mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage,
+                callingFeatureId);
         if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid,
                 callingUid, checkedOptions)) {
             // activity start was intercepted, e.g. because the target user is currently in quiet
@@ -1023,7 +1030,7 @@
             if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                     aInfo.packageName, userId)) {
                 final IIntentSender target = mService.getIntentSenderLocked(
-                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
+                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, callingFeatureId,
                         callingUid, userId, null, null, 0, new Intent[]{intent},
                         new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
                                 | PendingIntent.FLAG_ONE_SHOT, null);
@@ -1081,7 +1088,7 @@
         // app [on install success].
         if (rInfo != null && rInfo.auxiliaryInfo != null) {
             intent = createLaunchIntent(rInfo.auxiliaryInfo, request.ephemeralIntent,
-                    callingPackage, verificationBundle, resolvedType, userId);
+                    callingPackage, callingFeatureId, verificationBundle, resolvedType, userId);
             resolvedType = null;
             callingUid = realCallingUid;
             callingPid = realCallingPid;
@@ -1090,9 +1097,10 @@
         }
 
         final ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
-                callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
-                resultRecord, resultWho, requestCode, request.componentSpecified,
-                voiceSession != null, mSupervisor, checkedOptions, sourceRecord);
+                callingPackage, callingFeatureId, intent, resolvedType, aInfo,
+                mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode,
+                request.componentSpecified, voiceSession != null, mSupervisor, checkedOptions,
+                sourceRecord);
         mLastStartActivityRecord = r;
 
         if (r.appTimeTracker == null && sourceRecord != null) {
@@ -1302,21 +1310,22 @@
      * Creates a launch intent for the given auxiliary resolution data.
      */
     private @NonNull Intent createLaunchIntent(@Nullable AuxiliaryResolveInfo auxiliaryResponse,
-            Intent originalIntent, String callingPackage, Bundle verificationBundle,
-            String resolvedType, int userId) {
+            Intent originalIntent, String callingPackage, @Nullable String callingFeatureId,
+            Bundle verificationBundle, String resolvedType, int userId) {
         if (auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo) {
             // request phase two resolution
             PackageManagerInternal packageManager = mService.getPackageManagerInternalLocked();
             boolean isRequesterInstantApp = packageManager.isInstantApp(callingPackage, userId);
             packageManager.requestInstantAppResolutionPhaseTwo(
                     auxiliaryResponse, originalIntent, resolvedType, callingPackage,
-                    isRequesterInstantApp, verificationBundle, userId);
+                    callingFeatureId, isRequesterInstantApp, verificationBundle, userId);
         }
         return InstantAppResolver.buildEphemeralInstallerIntent(
                 originalIntent,
                 InstantAppResolver.sanitizeIntent(originalIntent),
                 auxiliaryResponse == null ? null : auxiliaryResponse.failureIntent,
                 callingPackage,
+                callingFeatureId,
                 verificationBundle,
                 resolvedType,
                 userId,
@@ -2575,6 +2584,11 @@
         return this;
     }
 
+    ActivityStarter setCallingFeatureId(String callingFeatureId) {
+        mRequest.callingFeatureId = callingFeatureId;
+        return this;
+    }
+
     /**
      * Sets the pid of the caller who requested to launch the activity.
      *
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 25f6d6f..7302e52 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -182,14 +182,15 @@
     public abstract void notifySingleTaskDisplayDrawn(int displayId);
 
     /**
-     * Start activity {@code intents} as if {@code packageName} on user {@code userId} did it.
+     * Start activity {@code intents} as if {@code packageName/featureId} on user {@code userId} did
+     * it.
      *
      * - DO NOT call it with the calling UID cleared.
      * - All the necessary caller permission checks must be done at callsites.
      *
      * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
      */
-    public abstract int startActivitiesAsPackage(String packageName,
+    public abstract int startActivitiesAsPackage(String packageName, String featureId,
             int userId, Intent[] intents, Bundle bOptions);
 
     /**
@@ -199,6 +200,7 @@
      * @param realCallingPid PID of the real caller.
      * @param realCallingUid UID of the real caller.
      * @param callingPackage Make a call as if this package did.
+     * @param callingFeatureId Make a call as if this feature in the package did.
      * @param intents Intents to start.
      * @param userId Start the intents on this user.
      * @param validateIncomingUser Set true to skip checking {@code userId} with the calling UID.
@@ -208,16 +210,17 @@
      *        from originatingPendingIntent
      */
     public abstract int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
-            String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo,
-            SafeActivityOptions options, int userId, boolean validateIncomingUser,
-            PendingIntentRecord originatingPendingIntent,
+            String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
+            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
             boolean allowBackgroundActivityStart);
 
     public abstract int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
-            String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
-            String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
-            int userId, Task inTask, String reason, boolean validateIncomingUser,
-            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart);
+            String callingPackage, @Nullable String callingFeaturId, Intent intent,
+            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
+            boolean allowBackgroundActivityStart);
 
     /**
      * Start activity {@code intent} without calling user-id check.
@@ -228,7 +231,7 @@
      * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
      */
     public abstract int startActivityAsUser(IApplicationThread caller, String callingPackage,
-            Intent intent, @Nullable Bundle options, int userId);
+            @Nullable String callingFeatureId, Intent intent, @Nullable Bundle options, int userId);
 
     /**
      * Called when Keyguard flags might have changed.
@@ -388,7 +391,7 @@
     public abstract ActivityTokens getTopActivityForTask(int taskId);
 
     public abstract IIntentSender getIntentSender(int type, String packageName,
-            int callingUid, int userId, IBinder token, String resultWho,
+            @Nullable String featureId, int callingUid, int userId, IBinder token, String resultWho,
             int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
             Bundle bOptions);
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 4b96ea0..1859fae 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -234,7 +234,6 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.AssistUtils;
-import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.ProcessMap;
 import com.android.internal.logging.MetricsLogger;
@@ -264,7 +263,6 @@
 import com.android.server.am.PendingIntentController;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.am.UserState;
-import com.android.server.appop.AppOpsService;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.inputmethod.InputMethodSystemProperty;
 import com.android.server.pm.UserManagerService;
@@ -378,7 +376,7 @@
     RootWindowContainer mRootWindowContainer;
     WindowManagerService mWindowManager;
     private UserManagerService mUserManager;
-    private AppOpsService mAppOpsService;
+    private AppOpsManager mAppOpsManager;
     /** All active uids in the system. */
     private final MirrorActiveUids mActiveUids = new MirrorActiveUids();
     private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>();
@@ -891,12 +889,11 @@
         return mUserManager;
     }
 
-    AppOpsService getAppOpsService() {
-        if (mAppOpsService == null) {
-            IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
-            mAppOpsService = (AppOpsService) IAppOpsService.Stub.asInterface(b);
+    AppOpsManager getAppOpsManager() {
+        if (mAppOpsManager == null) {
+            mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
         }
-        return mAppOpsService;
+        return mAppOpsManager;
     }
 
     boolean hasUserRestriction(String restriction, int userId) {
@@ -904,8 +901,8 @@
     }
 
     boolean hasSystemAlertWindowPermission(int callingUid, int callingPid, String callingPackage) {
-        final int mode = getAppOpsService().noteOperation(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
-                callingUid, callingPackage, /* featureId */ null, false, "");
+        final int mode = getAppOpsManager().noteOpNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+                callingUid, callingPackage, /* featureId */ null, "");
         if (mode == AppOpsManager.MODE_DEFAULT) {
             return checkPermission(Manifest.permission.SYSTEM_ALERT_WINDOW, callingPid, callingUid)
                     == PERMISSION_GRANTED;
@@ -1025,41 +1022,43 @@
 
     @Override
     public final int startActivity(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
-        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
-                resultWho, requestCode, startFlags, profilerInfo, bOptions,
+            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
+            Bundle bOptions) {
+        return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
+                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions,
                 UserHandle.getCallingUserId());
     }
 
     @Override
     public final int startActivities(IApplicationThread caller, String callingPackage,
-            Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions,
-            int userId) {
+            String callingFeatureId, Intent[] intents, String[] resolvedTypes, IBinder resultTo,
+            Bundle bOptions, int userId) {
         assertPackageMatchesCallingUid(callingPackage);
         final String reason = "startActivities";
         enforceNotIsolatedCaller(reason);
         userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason);
         // TODO: Switch to user app stacks here.
         return getActivityStartController().startActivities(caller, -1, 0, -1, callingPackage,
-                intents, resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId,
-                reason, null /* originatingPendingIntent */,
-                false /* allowBackgroundActivityStart */);
+                callingFeatureId, intents, resolvedTypes, resultTo,
+                SafeActivityOptions.fromBundle(bOptions), userId, reason,
+                null /* originatingPendingIntent */, false /* allowBackgroundActivityStart */);
     }
 
     @Override
     public int startActivityAsUser(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
-        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
-                resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
+            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
+            Bundle bOptions, int userId) {
+        return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
+                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
                 true /*validateIncomingUser*/);
     }
 
     private int startActivityAsUser(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
-            boolean validateIncomingUser) {
+            @Nullable String callingFeatureId, Intent intent, String resolvedType,
+            IBinder resultTo, String resultWho, int requestCode, int startFlags,
+            ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
         assertPackageMatchesCallingUid(callingPackage);
         enforceNotIsolatedCaller("startActivityAsUser");
 
@@ -1070,6 +1069,7 @@
         return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
                 .setCaller(caller)
                 .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setResultTo(resultTo)
                 .setResultWho(resultWho)
@@ -1215,6 +1215,7 @@
                     .setCallingPid(-1)
                     .setCallingUid(r.launchedFromUid)
                     .setCallingPackage(r.launchedFromPackage)
+                    .setCallingFeatureId(r.launchedFromFeatureId)
                     .setRealCallingPid(-1)
                     .setRealCallingUid(r.launchedFromUid)
                     .setActivityOptions(options)
@@ -1231,8 +1232,9 @@
 
     @Override
     public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
+            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
+            Bundle bOptions, int userId) {
         assertPackageMatchesCallingUid(callingPackage);
         final WaitResult res = new WaitResult();
         enforceNotIsolatedCaller("startActivityAndWait");
@@ -1242,6 +1244,7 @@
         getActivityStartController().obtainStarter(intent, "startActivityAndWait")
                 .setCaller(caller)
                 .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setResultTo(resultTo)
                 .setResultWho(resultWho)
@@ -1257,8 +1260,9 @@
 
     @Override
     public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
-            int startFlags, Configuration config, Bundle bOptions, int userId) {
+            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, Configuration config,
+            Bundle bOptions, int userId) {
         assertPackageMatchesCallingUid(callingPackage);
         enforceNotIsolatedCaller("startActivityWithConfig");
         userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
@@ -1267,6 +1271,7 @@
         return getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
                 .setCaller(caller)
                 .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setResultTo(resultTo)
                 .setResultWho(resultWho)
@@ -1317,6 +1322,7 @@
         final ActivityRecord sourceRecord;
         final int targetUid;
         final String targetPackage;
+        final String targetFeatureId;
         final boolean isResolver;
         synchronized (mGlobalLock) {
             if (resultTo == null) {
@@ -1384,6 +1390,7 @@
             }
             targetUid = sourceRecord.launchedFromUid;
             targetPackage = sourceRecord.launchedFromPackage;
+            targetFeatureId = sourceRecord.launchedFromFeatureId;
             isResolver = sourceRecord.isResolverOrChildActivity();
         }
 
@@ -1396,6 +1403,7 @@
             return getActivityStartController().obtainStarter(intent, "startActivityAsCaller")
                     .setCallingUid(targetUid)
                     .setCallingPackage(targetPackage)
+                    .setCallingFeatureId(targetFeatureId)
                     .setResolvedType(resolvedType)
                     .setResultTo(resultTo)
                     .setResultWho(resultWho)
@@ -1431,8 +1439,8 @@
     }
 
     @Override
-    public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
-            Intent intent, String resolvedType, IVoiceInteractionSession session,
+    public int startVoiceActivity(String callingPackage, String callingFeatureId, int callingPid,
+            int callingUid, Intent intent, String resolvedType, IVoiceInteractionSession session,
             IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
             Bundle bOptions, int userId) {
         assertPackageMatchesCallingUid(callingPackage);
@@ -1445,6 +1453,7 @@
         return getActivityStartController().obtainStarter(intent, "startVoiceActivity")
                 .setCallingUid(callingUid)
                 .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setVoiceSession(session)
                 .setVoiceInteractor(interactor)
@@ -1457,8 +1466,9 @@
     }
 
     @Override
-    public int startAssistantActivity(String callingPackage, int callingPid, int callingUid,
-            Intent intent, String resolvedType, Bundle bOptions, int userId) {
+    public int startAssistantActivity(String callingPackage, @NonNull String callingFeatureId,
+            int callingPid, int callingUid, Intent intent, String resolvedType, Bundle bOptions,
+            int userId) {
         assertPackageMatchesCallingUid(callingPackage);
         mAmInternal.enforceCallingPermission(BIND_VOICE_INTERACTION, "startAssistantActivity()");
         userId = handleIncomingUser(callingPid, callingUid, userId, "startAssistantActivity");
@@ -1466,6 +1476,7 @@
         return getActivityStartController().obtainStarter(intent, "startAssistantActivity")
                 .setCallingUid(callingUid)
                 .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setActivityOptions(bOptions)
                 .setUserId(userId)
@@ -1489,13 +1500,14 @@
         try {
             synchronized (mGlobalLock) {
                 final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
+                final String recentsFeatureId = mRecentTasks.getRecentsComponentFeatureId();
                 final int recentsUid = mRecentTasks.getRecentsComponentUid();
                 final WindowProcessController caller = getProcessController(callingPid, callingUid);
 
                 // Start a new recents animation
                 final RecentsAnimation anim = new RecentsAnimation(this, mStackSupervisor,
                         getActivityStartController(), mWindowManager, intent, recentsComponent,
-                        recentsUid, caller);
+                        recentsFeatureId, recentsUid, caller);
                 if (recentsAnimationRunner == null) {
                     anim.preloadRecentsActivity();
                 } else {
@@ -5775,9 +5787,9 @@
 
     }
 
-    IIntentSender getIntentSenderLocked(int type, String packageName, int callingUid, int userId,
-            IBinder token, String resultWho, int requestCode, Intent[] intents,
-            String[] resolvedTypes, int flags, Bundle bOptions) {
+    IIntentSender getIntentSenderLocked(int type, String packageName, String featureId,
+            int callingUid, int userId, IBinder token, String resultWho, int requestCode,
+            Intent[] intents, String[] resolvedTypes, int flags, Bundle bOptions) {
 
         ActivityRecord activity = null;
         if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
@@ -5793,8 +5805,8 @@
         }
 
         final PendingIntentRecord rec = mPendingIntentController.getIntentSender(type, packageName,
-                callingUid, userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
-                bOptions);
+                featureId, callingUid, userId, token, resultWho, requestCode, intents,
+                resolvedTypes, flags, bOptions);
         final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
         if (noCreate) {
             return rec;
@@ -6182,8 +6194,8 @@
         }
 
         @Override
-        public int startActivitiesAsPackage(String packageName, int userId, Intent[] intents,
-                Bundle bOptions) {
+        public int startActivitiesAsPackage(String packageName, @Nullable String featureId,
+                int userId, Intent[] intents, Bundle bOptions) {
             Objects.requireNonNull(intents, "intents");
             final String[] resolvedTypes = new String[intents.length];
 
@@ -6208,7 +6220,7 @@
             }
 
             return getActivityStartController().startActivitiesInPackage(
-                    packageUid, packageName,
+                    packageUid, packageName, featureId,
                     intents, resolvedTypes, null /* resultTo */,
                     SafeActivityOptions.fromBundle(bOptions), userId,
                     false /* validateIncomingUser */, null /* originatingPendingIntent */,
@@ -6217,41 +6229,41 @@
 
         @Override
         public int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
-                String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo,
-                SafeActivityOptions options, int userId, boolean validateIncomingUser,
-                PendingIntentRecord originatingPendingIntent,
+                String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
+                String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
+                boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
                 boolean allowBackgroundActivityStart) {
             assertPackageMatchesCallingUid(callingPackage);
             synchronized (mGlobalLock) {
                 return getActivityStartController().startActivitiesInPackage(uid, realCallingPid,
-                        realCallingUid, callingPackage, intents, resolvedTypes, resultTo, options,
-                        userId, validateIncomingUser, originatingPendingIntent,
+                        realCallingUid, callingPackage, callingFeatureId, intents, resolvedTypes,
+                        resultTo, options, userId, validateIncomingUser, originatingPendingIntent,
                         allowBackgroundActivityStart);
             }
         }
 
         @Override
         public int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
-                String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
-                String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
-                int userId, Task inTask, String reason, boolean validateIncomingUser,
-                PendingIntentRecord originatingPendingIntent,
+                String callingPackage, @Nullable String callingFeatureId, Intent intent,
+                String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+                int startFlags, SafeActivityOptions options, int userId, Task inTask, String reason,
+                boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
                 boolean allowBackgroundActivityStart) {
             assertPackageMatchesCallingUid(callingPackage);
             synchronized (mGlobalLock) {
                 return getActivityStartController().startActivityInPackage(uid, realCallingPid,
-                        realCallingUid, callingPackage, intent, resolvedType, resultTo, resultWho,
-                        requestCode, startFlags, options, userId, inTask, reason,
-                        validateIncomingUser, originatingPendingIntent,
+                        realCallingUid, callingPackage, callingFeatureId, intent, resolvedType,
+                        resultTo, resultWho, requestCode, startFlags, options, userId, inTask,
+                        reason, validateIncomingUser, originatingPendingIntent,
                         allowBackgroundActivityStart);
             }
         }
 
         @Override
-        public int startActivityAsUser(IApplicationThread caller, String callerPacakge,
-                Intent intent, Bundle options, int userId) {
+        public int startActivityAsUser(IApplicationThread caller, String callerPackage,
+                @Nullable String callerFeatureId, Intent intent, Bundle options, int userId) {
             return ActivityTaskManagerService.this.startActivityAsUser(
-                    caller, callerPacakge, intent,
+                    caller, callerPackage, callerFeatureId, intent,
                     intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                     null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options, userId,
                     false /*validateIncomingUser*/);
@@ -6687,12 +6699,12 @@
 
         @Override
         public IIntentSender getIntentSender(int type, String packageName,
-                int callingUid, int userId, IBinder token, String resultWho,
-                int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
-                Bundle bOptions) {
+                @Nullable String featureId, int callingUid, int userId, IBinder token,
+                String resultWho, int requestCode, Intent[] intents, String[] resolvedTypes,
+                int flags, Bundle bOptions) {
             synchronized (mGlobalLock) {
-                return getIntentSenderLocked(type, packageName, callingUid, userId, token,
-                        resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
+                return getIntentSenderLocked(type, packageName, featureId, callingUid, userId,
+                        token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 16a7564..8fa8119 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -125,7 +125,7 @@
     }
 
     @Override
-    public int startActivity(IBinder whoThread, String callingPackage,
+    public int startActivity(IBinder whoThread, String callingPackage, String callingFeatureId,
             Intent intent, String resolvedType, Bundle bOptions) {
         checkCaller();
         mService.assertPackageMatchesCallingUid(callingPackage);
@@ -148,6 +148,7 @@
         return mService.getActivityStartController().obtainStarter(intent, "AppTaskImpl")
                 .setCaller(appThread)
                 .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setActivityOptions(bOptions)
                 .setUserId(callingUser)
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 9b464c2..5385e2f 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -552,6 +552,10 @@
         return animationType;
     }
 
+    boolean isAnimationTypeFadeIn() {
+        return mAnimationType == FADE_IN;
+    }
+
     public Handler getHandler() {
         return mHandler;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index c78707a..ca6bd2d 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -57,8 +57,8 @@
 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
@@ -855,7 +855,7 @@
             case TYPE_WALLPAPER:
                 // Dreams and wallpapers don't have an app window token and can thus not be
                 // letterboxed. Hence always let them extend under the cutout.
-                attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+                attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
                 break;
             case TYPE_NOTIFICATION_SHADE:
                 // If the Keyguard is in a hidden state (occluded by another window), we force to
@@ -1511,15 +1511,16 @@
         // nav bar and ensure the application doesn't see the event.
         if (navVisible || navAllowedHidden) {
             if (mInputConsumer != null) {
+                mInputConsumer.dismiss();
                 mHandler.sendMessage(
                         mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
                 mInputConsumer = null;
             }
         } else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) {
-            mInputConsumer = mService.createInputConsumer(mHandler.getLooper(),
+            mInputConsumer = mDisplayContent.getInputMonitor().createInputConsumer(
+                    mHandler.getLooper(),
                     INPUT_CONSUMER_NAVIGATION,
-                    HideNavInputEventReceiver::new,
-                    displayFrames.mDisplayId);
+                    HideNavInputEventReceiver::new);
             // As long as mInputConsumer is active, hover events are not dispatched to the app
             // and the pointer icon is likely to become stale. Hide it to avoid confusion.
             InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
@@ -2298,15 +2299,24 @@
         // cropped / shifted to the displayFrame in WindowState.
         final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
                 && type != TYPE_BASE_APPLICATION;
-
         // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
         // the cutout safe zone.
-        if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
-                || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER) {
+        if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
             final Rect displayCutoutSafeExceptMaybeBars = sTmpDisplayCutoutSafeExceptMaybeBarsRect;
             displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
+            if (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES) {
+                if (displayFrames.mDisplayWidth < displayFrames.mDisplayHeight) {
+                    displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
+                    displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
+                } else {
+                    displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
+                    displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
+                }
+            }
+
             if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
-                    && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
+                    && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+                    || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
                 // At the top we have the status bar, so apps that are
                 // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN
                 // already expect that there's an inset there and we don't need to exclude
@@ -2314,7 +2324,8 @@
                 displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
             }
             if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation
-                    && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
+                    && (cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+                    || cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)) {
                 // Same for the navigation bar.
                 switch (mNavigationBarPosition) {
                     case NAV_BAR_BOTTOM:
@@ -3173,7 +3184,7 @@
 
     private void disposeInputConsumer(InputConsumer inputConsumer) {
         if (inputConsumer != null) {
-            inputConsumer.dismiss();
+            inputConsumer.dispose();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index faa6e52..b638e49 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -103,9 +103,17 @@
         @Override
         public void dismiss() {
             synchronized (mService.mGlobalLock) {
-                if (mInputMonitor.destroyInputConsumer(mWindowHandle.name)) {
-                    mInputEventReceiver.dispose();
-                }
+                mInputMonitor.mInputConsumers.remove(mName);
+                hide(mInputMonitor.mInputTransaction);
+                mInputMonitor.updateInputWindowsLw(true /* force */);
+            }
+        }
+
+        @Override
+        public void dispose() {
+            synchronized (mService.mGlobalLock) {
+                disposeChannelsLw();
+                mInputEventReceiver.dispose();
             }
         }
     }
@@ -415,18 +423,18 @@
     }
 
     private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
-        InputConsumerImpl navInputConsumer;
-        InputConsumerImpl pipInputConsumer;
-        InputConsumerImpl wallpaperInputConsumer;
-        InputConsumerImpl recentsAnimationInputConsumer;
+        InputConsumerImpl mNavInputConsumer;
+        InputConsumerImpl mPipInputConsumer;
+        InputConsumerImpl mWallpaperInputConsumer;
+        InputConsumerImpl mRecentsAnimationInputConsumer;
 
-        private boolean mAddInputConsumerHandle;
+        private boolean mAddNavInputConsumerHandle;
         private boolean mAddPipInputConsumerHandle;
         private boolean mAddWallpaperInputConsumerHandle;
         private boolean mAddRecentsAnimationInputConsumerHandle;
 
-        boolean inDrag;
-        WallpaperController wallpaperController;
+        boolean mInDrag;
+        WallpaperController mWallpaperController;
 
         // An invalid window handle that tells SurfaceFlinger not update the input info.
         final InputWindowHandle mInvalidInputWindow = new InputWindowHandle(null, mDisplayId);
@@ -434,20 +442,20 @@
         private void updateInputWindows(boolean inDrag) {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
 
-            navInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION);
-            pipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP);
-            wallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER);
-            recentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
+            mNavInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION);
+            mPipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP);
+            mWallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER);
+            mRecentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
 
-            mAddInputConsumerHandle = navInputConsumer != null;
-            mAddPipInputConsumerHandle = pipInputConsumer != null;
-            mAddWallpaperInputConsumerHandle = wallpaperInputConsumer != null;
-            mAddRecentsAnimationInputConsumerHandle = recentsAnimationInputConsumer != null;
+            mAddNavInputConsumerHandle = mNavInputConsumer != null;
+            mAddPipInputConsumerHandle = mPipInputConsumer != null;
+            mAddWallpaperInputConsumerHandle = mWallpaperInputConsumer != null;
+            mAddRecentsAnimationInputConsumerHandle = mRecentsAnimationInputConsumer != null;
 
             mTmpRect.setEmpty();
             mDisableWallpaperTouchEvents = false;
-            this.inDrag = inDrag;
-            wallpaperController = mDisplayContent.mWallpaperController;
+            mInDrag = inDrag;
+            mWallpaperController = mDisplayContent.mWallpaperController;
 
             resetInputConsumers(mInputTransaction);
 
@@ -455,7 +463,7 @@
                     true /* traverseTopToBottom */);
 
             if (mAddWallpaperInputConsumerHandle) {
-                wallpaperInputConsumer.show(mInputTransaction, 0);
+                mWallpaperInputConsumer.show(mInputTransaction, 0);
             }
 
             if (mApplyImmediately) {
@@ -494,8 +502,8 @@
                 if (recentsAnimationController != null
                         && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord)) {
                     if (recentsAnimationController.updateInputConsumerForApp(
-                            recentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
-                        recentsAnimationInputConsumer.show(mInputTransaction, w);
+                            mRecentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
+                        mRecentsAnimationInputConsumer.show(mInputTransaction, w);
                         mAddRecentsAnimationInputConsumerHandle = false;
                     }
                 }
@@ -505,25 +513,25 @@
                 if (mAddPipInputConsumerHandle) {
                     // Update the bounds of the Pip input consumer to match the window bounds.
                     w.getBounds(mTmpRect);
-                    pipInputConsumer.layout(mInputTransaction, mTmpRect);
+                    mPipInputConsumer.layout(mInputTransaction, mTmpRect);
 
                     // The touchable region is relative to the surface top-left
                     mTmpRect.offsetTo(0, 0);
-                    pipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
-                    pipInputConsumer.show(mInputTransaction, w);
+                    mPipInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
+                    mPipInputConsumer.show(mInputTransaction, w);
                     mAddPipInputConsumerHandle = false;
                 }
             }
 
-            if (mAddInputConsumerHandle) {
-                navInputConsumer.show(mInputTransaction, w);
-                mAddInputConsumerHandle = false;
+            if (mAddNavInputConsumerHandle) {
+                mNavInputConsumer.show(mInputTransaction, w);
+                mAddNavInputConsumerHandle = false;
             }
 
             if (mAddWallpaperInputConsumerHandle) {
                 if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisibleLw()) {
                     // Add the wallpaper input consumer above the first visible wallpaper.
-                    wallpaperInputConsumer.show(mInputTransaction, w);
+                    mWallpaperInputConsumer.show(mInputTransaction, w);
                     mAddWallpaperInputConsumerHandle = false;
                 }
             }
@@ -531,13 +539,13 @@
             if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) {
                 mDisableWallpaperTouchEvents = true;
             }
-            final boolean hasWallpaper = wallpaperController.isWallpaperTarget(w)
+            final boolean hasWallpaper = mWallpaperController.isWallpaperTarget(w)
                     && !mService.mPolicy.isKeyguardShowing()
                     && !mDisableWallpaperTouchEvents;
 
             // If there's a drag in progress and 'child' is a potential drop target,
             // make sure it's been told about the drag
-            if (inDrag && isVisible && w.getDisplayContent().isDefaultDisplay) {
+            if (mInDrag && isVisible && w.getDisplayContent().isDefaultDisplay) {
                 mService.mDragDropController.sendDragStartedIfNeededLocked(w);
             }
 
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index a9dc36d..3771b3e 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -42,6 +42,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.AppGlobals;
@@ -155,6 +156,7 @@
      */
     private int mRecentsUid = -1;
     private ComponentName mRecentsComponent = null;
+    private @Nullable String mFeatureId;
 
     /**
      * Mapping of user id -> whether recent tasks have been loaded for that user.
@@ -418,6 +420,13 @@
     }
 
     /**
+     * @return the featureId for the recents component.
+     */
+    @Nullable String getRecentsComponentFeatureId() {
+        return mFeatureId;
+    }
+
+    /**
      * @return the uid for the recents component.
      */
     int getRecentsComponentUid() {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 9770947..b0492be 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -63,6 +63,7 @@
     private final DisplayContent mDefaultDisplay;
     private final Intent mTargetIntent;
     private final ComponentName mRecentsComponent;
+    private final @Nullable String mRecentsFeatureId;
     private final int mRecentsUid;
     private final @Nullable WindowProcessController mCaller;
     private final int mUserId;
@@ -80,8 +81,8 @@
 
     RecentsAnimation(ActivityTaskManagerService atm, ActivityStackSupervisor stackSupervisor,
             ActivityStartController activityStartController, WindowManagerService wm,
-            Intent targetIntent, ComponentName recentsComponent, int recentsUid,
-            @Nullable WindowProcessController caller) {
+            Intent targetIntent, ComponentName recentsComponent, @Nullable String recentsFeatureId,
+            int recentsUid, @Nullable WindowProcessController caller) {
         mService = atm;
         mStackSupervisor = stackSupervisor;
         mDefaultDisplay = mService.mRootWindowContainer.getDefaultDisplay();
@@ -89,6 +90,7 @@
         mWindowManager = wm;
         mTargetIntent = targetIntent;
         mRecentsComponent = recentsComponent;
+        mRecentsFeatureId = recentsFeatureId;
         mRecentsUid = recentsUid;
         mCaller = caller;
         mUserId = atm.getCurrentUserId();
@@ -456,6 +458,7 @@
                 .obtainStarter(mTargetIntent, reason)
                 .setCallingUid(mRecentsUid)
                 .setCallingPackage(mRecentsComponent.getPackageName())
+                .setCallingFeatureId(mRecentsFeatureId)
                 .setActivityOptions(new SafeActivityOptions(options))
                 .setUserId(mUserId)
                 .execute();
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 326335e..9c23ed3 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -184,6 +184,7 @@
     private static final String ATTR_TASK_AFFILIATION_COLOR = "task_affiliation_color";
     private static final String ATTR_CALLING_UID = "calling_uid";
     private static final String ATTR_CALLING_PACKAGE = "calling_package";
+    private static final String ATTR_CALLING_FEATURE_ID = "calling_feature_id";
     private static final String ATTR_SUPPORTS_PICTURE_IN_PICTURE = "supports_picture_in_picture";
     private static final String ATTR_RESIZE_MODE = "resize_mode";
     private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds";
@@ -297,6 +298,7 @@
     // For relaunching the task from recents as though it was launched by the original launcher.
     int mCallingUid;
     String mCallingPackage;
+    String mCallingFeatureId;
 
     private final Rect mTmpStableBounds = new Rect();
     private final Rect mTmpNonDecorBounds = new Rect();
@@ -483,8 +485,8 @@
                 true /*neverRelinquishIdentity*/,
                 _taskDescription != null ? _taskDescription : new TaskDescription(),
                 _taskId, INVALID_TASK_ID, INVALID_TASK_ID, 0 /*taskAffiliationColor*/,
-                info.applicationInfo.uid, info.packageName, info.resizeMode,
-                info.supportsPictureInPicture(), false /*_realActivitySuspended*/,
+                info.applicationInfo.uid, info.packageName, null /* default featureId */,
+                info.resizeMode, info.supportsPictureInPicture(), false /*_realActivitySuspended*/,
                 false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE, info,
                 _voiceSession, _voiceInteractor, stack);
     }
@@ -506,18 +508,17 @@
     }
 
     /** Don't use constructor directly. This is only used by XML parser. */
-    Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
-            Intent _affinityIntent, String _affinity, String _rootAffinity,
-            ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
-            boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId,
-            int _effectiveUid, String _lastDescription,
+    Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent, Intent _affinityIntent,
+            String _affinity, String _rootAffinity, ComponentName _realActivity,
+            ComponentName _origActivity, boolean _rootWasReset, boolean _autoRemoveRecents,
+            boolean _askedCompatMode, int _userId, int _effectiveUid, String _lastDescription,
             long lastTimeMoved, boolean neverRelinquishIdentity,
             TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId,
             int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
-            int resizeMode, boolean supportsPictureInPicture, boolean _realActivitySuspended,
-            boolean userSetupComplete, int minWidth, int minHeight, ActivityInfo info,
-            IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
-            ActivityStack stack) {
+            @Nullable String callingFeatureId, int resizeMode, boolean supportsPictureInPicture,
+            boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight,
+            ActivityInfo info, IVoiceInteractionSession _voiceSession,
+            IVoiceInteractor _voiceInteractor, ActivityStack stack) {
         super(atmService.mWindowManager);
 
         EventLogTags.writeWmTaskCreated(_taskId, stack != null ? getRootTaskId() : INVALID_TASK_ID);
@@ -556,6 +557,7 @@
         mNextAffiliateTaskId = nextTaskId;
         mCallingUid = callingUid;
         mCallingPackage = callingPackage;
+        mCallingFeatureId = callingFeatureId;
         mResizeMode = resizeMode;
         if (info != null) {
             setIntent(_intent, info);
@@ -902,6 +904,7 @@
     void setIntent(ActivityRecord r) {
         mCallingUid = r.launchedFromUid;
         mCallingPackage = r.launchedFromPackage;
+        mCallingFeatureId = r.launchedFromFeatureId;
         setIntent(r.intent, r.info);
         setLockTaskAuth(r);
 
@@ -1357,6 +1360,7 @@
             isPersistable = r.isPersistable();
             mCallingUid = r.launchedFromUid;
             mCallingPackage = r.launchedFromPackage;
+            mCallingFeatureId = r.launchedFromFeatureId;
             // Clamp to [1, max].
             maxRecents = Math.min(Math.max(r.info.maxRecents, 1),
                     ActivityTaskManager.getMaxAppRecentsLimitStatic());
@@ -3261,6 +3265,7 @@
         pw.print(" mCallingUid="); UserHandle.formatUid(pw, mCallingUid);
         pw.print(" mUserSetupComplete="); pw.print(mUserSetupComplete);
         pw.print(" mCallingPackage="); pw.println(mCallingPackage);
+        pw.print(" mCallingFeatureId="); pw.println(mCallingFeatureId);
         if (affinity != null || rootAffinity != null) {
             pw.print(prefix); pw.print("affinity="); pw.print(affinity);
             if (affinity == null || !affinity.equals(rootAffinity)) {
@@ -3470,6 +3475,8 @@
         out.attribute(null, ATTR_NEXT_AFFILIATION, String.valueOf(mNextAffiliateTaskId));
         out.attribute(null, ATTR_CALLING_UID, String.valueOf(mCallingUid));
         out.attribute(null, ATTR_CALLING_PACKAGE, mCallingPackage == null ? "" : mCallingPackage);
+        out.attribute(null, ATTR_CALLING_FEATURE_ID,
+                mCallingFeatureId == null ? "" : mCallingFeatureId);
         out.attribute(null, ATTR_RESIZE_MODE, String.valueOf(mResizeMode));
         out.attribute(null, ATTR_SUPPORTS_PICTURE_IN_PICTURE,
                 String.valueOf(mSupportsPictureInPicture));
@@ -3583,16 +3590,17 @@
                 long lastTimeMoved, boolean neverRelinquishIdentity,
                 TaskDescription lastTaskDescription, int taskAffiliation, int prevTaskId,
                 int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
-                int resizeMode, boolean supportsPictureInPicture, boolean realActivitySuspended,
+                @Nullable String callingFeatureId, int resizeMode,
+                boolean supportsPictureInPicture, boolean realActivitySuspended,
                 boolean userSetupComplete, int minWidth, int minHeight, ActivityStack stack) {
             return new ActivityStack(service, taskId, intent, affinityIntent, affinity,
                     rootAffinity, realActivity, origActivity, rootWasReset, autoRemoveRecents,
                     askedCompatMode, userId, effectiveUid, lastDescription,
                     lastTimeMoved, neverRelinquishIdentity, lastTaskDescription, taskAffiliation,
                     prevTaskId, nextTaskId, taskAffiliationColor, callingUid, callingPackage,
-                    resizeMode, supportsPictureInPicture, realActivitySuspended, userSetupComplete,
-                    minWidth, minHeight, null /*ActivityInfo*/, null /*_voiceSession*/,
-                    null /*_voiceInteractor*/, stack);
+                    callingFeatureId, resizeMode, supportsPictureInPicture, realActivitySuspended,
+                    userSetupComplete, minWidth, minHeight, null /*ActivityInfo*/,
+                    null /*_voiceSession*/, null /*_voiceInteractor*/, stack);
         }
 
         Task restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
@@ -3625,6 +3633,7 @@
             int nextTaskId = INVALID_TASK_ID;
             int callingUid = -1;
             String callingPackage = "";
+            String callingFeatureId = null;
             int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
             boolean supportsPictureInPicture = false;
             Rect lastNonFullscreenBounds = null;
@@ -3705,6 +3714,9 @@
                     case ATTR_CALLING_PACKAGE:
                         callingPackage = attrValue;
                         break;
+                    case ATTR_CALLING_FEATURE_ID:
+                        callingFeatureId = attrValue;
+                        break;
                     case ATTR_RESIZE_MODE:
                         resizeMode = Integer.parseInt(attrValue);
                         break;
@@ -3806,8 +3818,8 @@
                     autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
                     lastTimeOnTop, neverRelinquishIdentity, taskDescription,
                     taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
-                    callingPackage, resizeMode, supportsPictureInPicture, realActivitySuspended,
-                    userSetupComplete, minWidth, minHeight, null /*stack*/);
+                    callingPackage, callingFeatureId, resizeMode, supportsPictureInPicture,
+                    realActivitySuspended, userSetupComplete, minWidth, minHeight, null /*stack*/);
             task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
             task.setBounds(lastNonFullscreenBounds);
 
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
index add11d6..369db05 100644
--- a/services/core/java/com/android/server/wm/TaskTile.java
+++ b/services/core/java/com/android/server/wm/TaskTile.java
@@ -60,10 +60,11 @@
                 System.currentTimeMillis(), true /*neverRelinquishIdentity*/,
                 new ActivityManager.TaskDescription(), id, INVALID_TASK_ID, INVALID_TASK_ID,
                 0 /*taskAffiliationColor*/, 0 /*callingUid*/, "" /*callingPackage*/,
-                RESIZE_MODE_RESIZEABLE, false /*supportsPictureInPicture*/,
-                false /*_realActivitySuspended*/, false /*userSetupComplete*/, INVALID_MIN_SIZE,
-                INVALID_MIN_SIZE, createEmptyActivityInfo(), null /*voiceSession*/,
-                null /*voiceInteractor*/, null /*stack*/);
+                null /*callingFeatureId*/, RESIZE_MODE_RESIZEABLE,
+                false /*supportsPictureInPicture*/, false /*_realActivitySuspended*/,
+                false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE,
+                createEmptyActivityInfo(), null /*voiceSession*/, null /*voiceInteractor*/,
+                null /*stack*/);
         getRequestedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d98c18c..8f9caea 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -230,7 +230,6 @@
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputEvent;
-import android.view.InputEventReceiver;
 import android.view.InputWindowHandle;
 import android.view.InsetsState;
 import android.view.KeyEvent;
@@ -5763,20 +5762,6 @@
     }
 
     @Override
-    public WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name,
-            InputEventReceiver.Factory inputEventReceiverFactory, int displayId) {
-        synchronized (mGlobalLock) {
-            DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-            if (displayContent != null) {
-                return displayContent.getInputMonitor().createInputConsumer(looper, name,
-                        inputEventReceiverFactory);
-            } else {
-                return null;
-            }
-        }
-    }
-
-    @Override
     public void createInputConsumer(IBinder token, String name, int displayId,
             InputChannel inputChannel) {
         synchronized (mGlobalLock) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f700d71..d2ec436 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -81,6 +81,8 @@
 import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
 import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
 import static android.app.admin.DevicePolicyManager.WIPE_SILENTLY;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.provider.Settings.Global.PRIVATE_DNS_MODE;
 import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
@@ -92,6 +94,7 @@
 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
+import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
 import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -162,6 +165,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.CrossProfileApps;
+import android.content.pm.CrossProfileAppsInternal;
 import android.content.pm.IPackageDataObserver;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
@@ -174,6 +178,7 @@
 import android.content.pm.ServiceInfo;
 import android.content.pm.StringParceledListSlice;
 import android.content.pm.UserInfo;
+import android.content.pm.parsing.AndroidPackage;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.database.Cursor;
@@ -408,6 +413,9 @@
     private static final String ATTR_APPLICATION_RESTRICTIONS_MANAGER
             = "application-restrictions-manager";
 
+    private static final String CALLED_FROM_PARENT = "calledFromParent";
+    private static final String NOT_CALLED_FROM_PARENT = "notCalledFromParent";
+
     // Comprehensive list of delegations.
     private static final String DELEGATIONS[] = {
         DELEGATION_CERT_INSTALL,
@@ -3968,7 +3976,7 @@
     @Override
     void handleStartUser(int userId) {
         updateScreenCaptureDisabled(userId,
-                getScreenCaptureDisabled(null, userId));
+                getScreenCaptureDisabled(null, userId, false));
         pushUserRestrictions(userId);
         // When system user is started (device boot), load cache for all users.
         // This is to mitigate the potential race between loading the cache and keyguard
@@ -4471,7 +4479,7 @@
                 .createEvent(DevicePolicyEnums.SET_PASSWORD_QUALITY)
                 .setAdmin(who)
                 .setInt(quality)
-                .setBoolean(parent)
+                .setStrings(parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
                 .write();
     }
 
@@ -5259,8 +5267,9 @@
     public int getPasswordComplexity(boolean parent) {
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.GET_USER_PASSWORD_COMPLEXITY_LEVEL)
-                .setStrings(mInjector.getPackageManager()
-                        .getPackagesForUid(mInjector.binderGetCallingUid()))
+                .setStrings(parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT,
+                        mInjector.getPackageManager().getPackagesForUid(
+                                mInjector.binderGetCallingUid()))
                 .write();
         final int callingUserId = mInjector.userHandleGetCallingUserId();
 
@@ -6934,6 +6943,7 @@
                 .createEvent(DevicePolicyEnums.WIPE_DATA_WITH_REASON)
                 .setAdmin(admin.info.getComponent())
                 .setInt(flags)
+                .setStrings(calledOnParentInstance ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
                 .write();
         String internalReason = String.format(
                 "DevicePolicyManager.wipeDataWithReason() from %s, organization-owned? %s",
@@ -7577,7 +7587,7 @@
      * Set whether the screen capture is disabled for the user managed by the specified admin.
      */
     @Override
-    public void setScreenCaptureDisabled(ComponentName who, boolean disabled) {
+    public void setScreenCaptureDisabled(ComponentName who, boolean disabled, boolean parent) {
         if (!mHasFeature) {
             return;
         }
@@ -7585,11 +7595,15 @@
         final int userHandle = UserHandle.getCallingUserId();
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(who,
-                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
+            if (parent) {
+                enforceProfileOwnerOfOrganizationOwnedDevice(ap);
+            }
             if (ap.disableScreenCapture != disabled) {
                 ap.disableScreenCapture = disabled;
                 saveSettingsLocked(userHandle);
-                updateScreenCaptureDisabled(userHandle, disabled);
+                final int affectedUserId = parent ? getProfileParentId(userHandle) : userHandle;
+                updateScreenCaptureDisabled(affectedUserId, disabled);
             }
         }
         DevicePolicyEventLogger
@@ -7604,20 +7618,25 @@
      * active admin (if given admin is null).
      */
     @Override
-    public boolean getScreenCaptureDisabled(ComponentName who, int userHandle) {
+    public boolean getScreenCaptureDisabled(ComponentName who, int userHandle, boolean parent) {
         if (!mHasFeature) {
             return false;
         }
         synchronized (getLockObject()) {
+            if (parent) {
+                final ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+                        DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER, parent);
+                enforceProfileOwnerOfOrganizationOwnedDevice(ap);
+            }
             if (who != null) {
-                ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
-                return (admin != null) ? admin.disableScreenCapture : false;
+                ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
+                return (admin != null) && admin.disableScreenCapture;
             }
 
-            DevicePolicyData policy = getUserData(userHandle);
-            final int N = policy.mAdminList.size();
-            for (int i = 0; i < N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
+            boolean includeParent = isOrganizationOwnedDeviceWithManagedProfile()
+                    && !isManagedProfile(userHandle);
+            List<ActiveAdmin> admins = getActiveAdminsForAffectedUser(userHandle, includeParent);
+            for (ActiveAdmin admin: admins) {
                 if (admin.disableScreenCapture) {
                     return true;
                 }
@@ -7628,14 +7647,11 @@
 
     private void updateScreenCaptureDisabled(int userHandle, boolean disabled) {
         mPolicyCache.setScreenCaptureDisabled(userHandle, disabled);
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    mInjector.getIWindowManager().refreshScreenCaptureDisabled(userHandle);
-                } catch (RemoteException e) {
-                    Log.w(LOG_TAG, "Unable to notify WindowManager.", e);
-                }
+        mHandler.post(() -> {
+            try {
+                mInjector.getIWindowManager().refreshScreenCaptureDisabled(userHandle);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, "Unable to notify WindowManager.", e);
             }
         });
     }
@@ -8093,6 +8109,7 @@
                 .createEvent(DevicePolicyEnums.SET_CAMERA_DISABLED)
                 .setAdmin(who)
                 .setBoolean(disabled)
+                .setStrings(parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
                 .write();
     }
 
@@ -8170,7 +8187,7 @@
                 .createEvent(DevicePolicyEnums.SET_KEYGUARD_DISABLED_FEATURES)
                 .setAdmin(who)
                 .setInt(which)
-                .setBoolean(parent)
+                .setStrings(parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
                 .write();
     }
 
@@ -8887,9 +8904,12 @@
                 UserInfo parent = mUserManager.getProfileParent(userId);
                 Intent intent = new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED);
                 intent.putExtra(Intent.EXTRA_USER, new UserHandle(userId));
+                UserHandle parentHandle = new UserHandle(parent.id);
+                mLocalService.broadcastIntentToCrossProfileManifestReceiversAsUser(intent,
+                        parentHandle, /* requiresPermission= */ true);
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
                         Intent.FLAG_RECEIVER_FOREGROUND);
-                mContext.sendBroadcastAsUser(intent, new UserHandle(parent.id));
+                mContext.sendBroadcastAsUser(intent, parentHandle);
             });
         }
     }
@@ -10677,7 +10697,7 @@
         DevicePolicyEventLogger
                 .createEvent(eventId)
                 .setAdmin(who)
-                .setStrings(key)
+                .setStrings(key, parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
                 .write();
         if (SecurityLog.isLoggingEnabled()) {
             final int eventTag = enabledFromThisOwner
@@ -10821,8 +10841,8 @@
                 .createEvent(DevicePolicyEnums.SET_APPLICATION_HIDDEN)
                 .setAdmin(callerPackage)
                 .setBoolean(isDelegate)
-                .setBoolean(parent)
-                .setStrings(packageName, hidden ? "hidden" : "not_hidden")
+                .setStrings(packageName, hidden ? "hidden" : "not_hidden",
+                        parent ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT)
                 .write();
         return result;
     }
@@ -12251,6 +12271,105 @@
             return DevicePolicyManagerService.this.getAllCrossProfilePackages();
         }
 
+        /**
+         * Sends the {@code intent} to the packages with cross profile capabilities.
+         *
+         * <p>This means the application must have the {@code crossProfile} property and
+         * and at least one of the following permissions:
+         *
+         * <ul>
+         *     <li>{@link android.Manifest.permission.INTERACT_ACROSS_PROFILES}
+         *     <li>{@link android.Manifest.permission.INTERACT_ACROSS_USERS}
+         *     <li>{@link android.Manifest.permission.INTERACT_ACROSS_USERS_FULL} permission or the
+         *     {@link AppOpsManager.OP_INTERACT_ACROSS_PROFILES} app operation authorization.
+         * </ul>
+         *
+         * <p>Note: The intent itself is not modified but copied before use.
+         *
+         * @param intent Template for the intent sent to the packages.
+         * @param parentHandle Handle of the user that will receive the intents.
+         * @param requiresPermission If false, all packages with the {@code crossProfile} property
+         *                           will receive the intent.
+         */
+        @Override
+        public void broadcastIntentToCrossProfileManifestReceiversAsUser(Intent intent,
+                UserHandle parentHandle, boolean requiresPermission) {
+            Objects.requireNonNull(intent);
+            Objects.requireNonNull(parentHandle);
+            final int userId = parentHandle.getIdentifier();
+            Slog.i(LOG_TAG,
+                    String.format("Sending %s broadcast to manifest receivers.",
+                            intent.getAction()));
+            try {
+                final List<ResolveInfo> receivers = mIPackageManager.queryIntentReceivers(
+                        intent, /* resolvedType= */ null,
+                        STOCK_PM_FLAGS, parentHandle.getIdentifier()).getList();
+                for (ResolveInfo receiver : receivers) {
+                    final String packageName = receiver.getComponentInfo().packageName;
+                    if (checkCrossProfilePackagePermissions(packageName, userId,
+                            requiresPermission)) {
+                        Slog.i(LOG_TAG,
+                                String.format("Sending %s broadcast to %s.", intent.getAction(),
+                                        packageName));
+                        final Intent packageIntent = new Intent(intent)
+                                .setComponent(receiver.getComponentInfo().getComponentName())
+                                .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+                        mContext.sendBroadcastAsUser(packageIntent, parentHandle);
+                    }
+                }
+            } catch (RemoteException ex) {
+                Slog.w(LOG_TAG,
+                        String.format("Cannot get list of broadcast receivers for %s because: %s.",
+                                intent.getAction(), ex));
+            }
+        }
+
+        /**
+         * Checks whether the package {@code packageName} has the required permissions to receive
+         * cross-profile broadcasts on behalf of the user {@code userId}.
+         */
+        private boolean checkCrossProfilePackagePermissions(String packageName,
+                @UserIdInt int userId, boolean requiresPermission) {
+            final PackageManagerInternal pmInternal = LocalServices.getService(
+                    PackageManagerInternal.class);
+            final AndroidPackage androidPackage = pmInternal.getPackage(packageName);
+            if (androidPackage == null || !androidPackage.isCrossProfile()) {
+                return false;
+            }
+            if (!requiresPermission) {
+                return true;
+            }
+            if (!isPackageEnabled(packageName, userId)) {
+                return false;
+            }
+            try {
+                final CrossProfileAppsInternal crossProfileAppsService = LocalServices.getService(
+                        CrossProfileAppsInternal.class);
+                return crossProfileAppsService.verifyPackageHasInteractAcrossProfilePermission(
+                        packageName, userId);
+            } catch (NameNotFoundException ex) {
+                Slog.w(LOG_TAG,
+                        String.format("Cannot find the package %s to check for permissions.",
+                                packageName));
+                return false;
+            }
+        }
+
+        private boolean isPackageEnabled(String packageName, @UserIdInt int userId) {
+            final int callingUid = Binder.getCallingUid();
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                final PackageInfo info = mInjector.getPackageManagerInternal()
+                        .getPackageInfo(
+                                packageName,
+                                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+                                callingUid,
+                                userId);
+                return info != null && info.applicationInfo.enabled;
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
     }
 
     private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp
index 323e7f1..b13d330 100644
--- a/services/incremental/Android.bp
+++ b/services/incremental/Android.bp
@@ -43,6 +43,7 @@
         "service.incremental.proto",
         "libutils",
         "libvold_binder",
+        "libc++fs",
     ],
     shared_libs: [
         "libandroidfw",
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 42dc1ba..980ae08 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -35,6 +35,7 @@
 #include <zlib.h>
 
 #include <ctime>
+#include <filesystem>
 #include <iterator>
 #include <span>
 #include <stack>
@@ -45,6 +46,7 @@
 
 using namespace std::literals;
 using namespace android::content::pm;
+namespace fs = std::filesystem;
 
 namespace android::incremental {
 
@@ -56,6 +58,7 @@
 struct Constants {
     static constexpr auto backing = "backing_store"sv;
     static constexpr auto mount = "mount"sv;
+    static constexpr auto mountKeyPrefix = "MT_"sv;
     static constexpr auto storagePrefix = "st"sv;
     static constexpr auto mountpointMdPrefix = ".mountpoint."sv;
     static constexpr auto infoMdName = ".info"sv;
@@ -104,7 +107,7 @@
     std::string res(path);
     std::replace(res.begin(), res.end(), '/', '_');
     std::replace(res.begin(), res.end(), '@', '_');
-    return res;
+    return std::string(constants().mountKeyPrefix) + res;
 }
 
 static std::pair<std::string, std::string> makeMountDir(std::string_view incrementalDir,
@@ -235,9 +238,7 @@
     if (!mIncrementalManager) {
         LOG(FATAL) << "IncrementalManager service is unavailable";
     }
-    // TODO(b/136132412): check that root dir should already exist
-    // TODO(b/136132412): enable mount existing dirs after SELinux rules are merged
-    // mountExistingImages();
+    mountExistingImages();
 }
 
 FileId IncrementalService::idFromMetadata(std::span<const uint8_t> metadata) {
@@ -328,21 +329,14 @@
     }
 
     std::thread([this, mounts = std::move(mounts)]() {
-        std::vector<IfsMountPtr> failedLoaderMounts;
         for (auto&& ifs : mounts) {
             if (prepareDataLoader(*ifs)) {
                 LOG(INFO) << "Successfully started data loader for mount " << ifs->mountId;
             } else {
+                // TODO(b/133435829): handle data loader start failures
                 LOG(WARNING) << "Failed to start data loader for mount " << ifs->mountId;
-                failedLoaderMounts.push_back(std::move(ifs));
             }
         }
-
-        while (!failedLoaderMounts.empty()) {
-            LOG(WARNING) << "Deleting failed mount " << failedLoaderMounts.back()->mountId;
-            deleteStorage(*failedLoaderMounts.back());
-            failedLoaderMounts.pop_back();
-        }
         mPrepareDataLoaders.set_value_at_thread_exit();
     }).detach();
     return mPrepareDataLoaders.get_future();
@@ -361,10 +355,9 @@
     }
 }
 
-StorageId IncrementalService::createStorage(std::string_view mountPoint,
-                                            DataLoaderParamsParcel&& dataLoaderParams,
-                                            const DataLoaderStatusListener& dataLoaderStatusListener,
-                                            CreateOptions options) {
+StorageId IncrementalService::createStorage(
+        std::string_view mountPoint, DataLoaderParamsParcel&& dataLoaderParams,
+        const DataLoaderStatusListener& dataLoaderStatusListener, CreateOptions options) {
     LOG(INFO) << "createStorage: " << mountPoint << " | " << int(options);
     if (!path::isAbsolute(mountPoint)) {
         LOG(ERROR) << "path is not absolute: " << mountPoint;
@@ -826,9 +819,10 @@
         metadata::BindPoint bp;
         bp.set_storage_id(storage);
         bp.set_allocated_dest_path(&target);
-        bp.set_source_subdir(std::string(path::relativize(storageRoot, source)));
+        bp.set_allocated_source_subdir(&source);
         const auto metadata = bp.SerializeAsString();
         bp.release_dest_path();
+        bp.release_source_subdir();
         mdFileName = makeBindMdName();
         auto node =
                 mIncFs->makeFile(ifs.control, path::join(ifs.root, constants().mount, mdFileName),
@@ -943,25 +937,20 @@
 }
 
 void IncrementalService::mountExistingImages() {
-    auto d = std::unique_ptr<DIR, decltype(&::closedir)>(::opendir(mIncrementalDir.c_str()),
-                                                         ::closedir);
-    while (auto e = ::readdir(d.get())) {
-        if (e->d_type != DT_DIR) {
+    for (const auto& entry : fs::directory_iterator(mIncrementalDir)) {
+        const auto path = entry.path().u8string();
+        const auto name = entry.path().filename().u8string();
+        if (!base::StartsWith(name, constants().mountKeyPrefix)) {
             continue;
         }
-        if (e->d_name == "."sv || e->d_name == ".."sv) {
-            continue;
-        }
-        auto root = path::join(mIncrementalDir, e->d_name);
-        if (!mountExistingImage(root, e->d_name)) {
-            IncFsMount::cleanupFilesystem(root);
+        const auto root = path::join(mIncrementalDir, name);
+        if (!mountExistingImage(root, name)) {
+            IncFsMount::cleanupFilesystem(path);
         }
     }
 }
 
 bool IncrementalService::mountExistingImage(std::string_view root, std::string_view key) {
-    LOG(INFO) << "Trying to mount: " << key;
-
     auto mountTarget = path::join(root, constants().mount);
     const auto backing = path::join(root, constants().backing);
 
@@ -1045,16 +1034,6 @@
         return false;
     }
 
-    DataLoaderParamsParcel dlParams;
-    dlParams.type = (DataLoaderType)m.loader().type();
-    dlParams.packageName = std::move(*m.mutable_loader()->mutable_package_name());
-    dlParams.className = std::move(*m.mutable_loader()->mutable_class_name());
-    dlParams.arguments = std::move(*m.mutable_loader()->mutable_arguments());
-    if (!prepareDataLoader(*ifs, &dlParams)) {
-        deleteStorage(*ifs);
-        return false;
-    }
-
     mMounts[ifs->mountId] = std::move(ifs);
     return true;
 }
@@ -1104,7 +1083,8 @@
     fsControlParcel.incremental->pendingReads.reset(
             base::unique_fd(::dup(ifs.control.pendingReads)));
     fsControlParcel.incremental->log.reset(base::unique_fd(::dup(ifs.control.logs)));
-    sp<IncrementalDataLoaderListener> listener = new IncrementalDataLoaderListener(*this, *externalListener);
+    sp<IncrementalDataLoaderListener> listener =
+            new IncrementalDataLoaderListener(*this, *externalListener);
     bool created = false;
     auto status = mIncrementalManager->prepareDataLoader(ifs.mountId, fsControlParcel, *dlp,
                                                          listener, &created);
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index f7598f7..3aa846c 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -266,28 +266,6 @@
     DataLoaderParamsParcel mDataLoaderParcel;
 };
 
-/*
-TEST_F(IncrementalServiceTest, testBootMountExistingImagesSuccess) {
-    TemporaryDir tempDir;
-    setUpExistingMountDir(tempDir.path);
-    mVold->mountIncFsSuccess();
-    mVold->bindMountSuccess();
-    mIncrementalManager->prepareDataLoaderSuccess();
-    ON_CALL(*mIncrementalManager, destroyDataLoader(_)).WillByDefault(Return(binder::Status::ok()));
-
-    EXPECT_CALL(*mVold, mountIncFs(_, _, _, _)).Times(1);
-    EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(1);
-
-    MockServiceManager serviceManager = MockServiceManager(mVold, mIncrementalManager, mIncFs);
-    std::unique_ptr<IncrementalService> incrementalService =
-            std::make_unique<IncrementalService>(serviceManager, tempDir.path);
-    auto finished = incrementalService->onSystemReady();
-    if (finished) {
-        finished->wait();
-    }
-}
-*/
-
 TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsFails) {
     mVold->mountIncFsFails();
     EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0);
@@ -410,7 +388,7 @@
 
     std::string tempPath(tempDir.path);
     std::replace(tempPath.begin(), tempPath.end(), '/', '_');
-    std::string mount_dir = std::string(mRootDir.path) + "/" + tempPath.substr(1);
+    std::string mount_dir = std::string(mRootDir.path) + "/MT_" + tempPath.substr(1);
     std::string normalized_dir_path = mount_dir + "/mount/st_1_0/" + dir_path;
 
     // Expecting incfs to call makeDir on a path like:
@@ -436,7 +414,7 @@
 
     std::string tempPath(tempDir.path);
     std::replace(tempPath.begin(), tempPath.end(), '/', '_');
-    std::string mount_dir = std::string(mRootDir.path) + "/" + tempPath.substr(1);
+    std::string mount_dir = std::string(mRootDir.path) + "/MT_" + tempPath.substr(1);
 
     InSequence seq;
     auto parent_path = std::string(first) + "/" + std::string(second);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
index 3975f0b..e3453a0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
@@ -50,6 +50,7 @@
 @RunWith(AndroidJUnit4.class)
 public class PendingIntentControllerTest {
     private static final String TEST_PACKAGE_NAME = "test-package-1";
+    private static final String TEST_FEATURE_ID = "test-feature-1";
     private static final int TEST_CALLING_UID = android.os.Process.myUid();
     private static final Intent[] TEST_INTENTS = new Intent[]{new Intent("com.test.intent")};
 
@@ -87,8 +88,8 @@
 
     private PendingIntentRecord createPendingIntentRecord(int flags) {
         return mPendingIntentController.getIntentSender(ActivityManager.INTENT_SENDER_BROADCAST,
-                TEST_PACKAGE_NAME, TEST_CALLING_UID, 0, null, null, 0, TEST_INTENTS, null, flags,
-                null);
+                TEST_PACKAGE_NAME, TEST_FEATURE_ID, TEST_CALLING_UID, 0, null, null, 0,
+                TEST_INTENTS, null, flags, null);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index 089a79b..a871ec6 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -178,6 +178,7 @@
                 new Intent(),
                 null /* callerApp */,
                 null  /* callerPackage */,
+                null /* callerFeatureId */,
                 0 /* callingPid */,
                 0 /* callingUid */,
                 false /* callerInstantApp */,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 39a749f..3f74681 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -5689,6 +5689,9 @@
     }
 
     public void testGetPasswordComplexity_securityExceptionNotThrownForParentInstance() {
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn(
+                new String[0]);
         mServiceContext.permissions.add(permission.REQUEST_PASSWORD_COMPLEXITY);
         setAsProfileOwner(admin1);
 
@@ -5698,12 +5701,18 @@
     }
 
     public void testGetPasswordComplexity_illegalStateExceptionIfLocked() {
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn(
+                new String[0]);
         when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
                 .thenReturn(false);
         assertThrows(IllegalStateException.class, () -> dpm.getPasswordComplexity());
     }
 
     public void testGetPasswordComplexity_securityExceptionWithoutPermissions() {
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn(
+                new String[0]);
         when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
                 .thenReturn(true);
         assertThrows(SecurityException.class, () -> dpm.getPasswordComplexity());
@@ -5711,6 +5720,9 @@
 
 
     public void testGetPasswordComplexity_currentUserNoPassword() {
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn(
+                new String[0]);
         when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
                 .thenReturn(true);
         mServiceContext.permissions.add(permission.REQUEST_PASSWORD_COMPLEXITY);
@@ -5721,6 +5733,9 @@
     }
 
     public void testGetPasswordComplexity_currentUserHasPassword() {
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn(
+                new String[0]);
         when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
                 .thenReturn(true);
         mServiceContext.permissions.add(permission.REQUEST_PASSWORD_COMPLEXITY);
@@ -5734,6 +5749,9 @@
     }
 
     public void testGetPasswordComplexity_unifiedChallengeReturnsParentUserPassword() {
+        mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+        when(getServices().packageManager.getPackagesForUid(DpmMockContext.CALLER_UID)).thenReturn(
+                new String[0]);
         when(getServices().userManager.isUserUnlocked(DpmMockContext.CALLER_USER_HANDLE))
                 .thenReturn(true);
         mServiceContext.permissions.add(permission.REQUEST_PASSWORD_COMPLEXITY);
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 77f842a..df2b3ef 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -24,6 +24,7 @@
 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.set;
 
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
@@ -1728,6 +1729,7 @@
         final ArgumentCaptor<Intent[]> intentsCaptor = ArgumentCaptor.forClass(Intent[].class);
         verify(mMockActivityTaskManagerInternal).startActivitiesAsPackage(
                 eq(packageName),
+                isNull(),
                 eq(userId),
                 intentsCaptor.capture(),
                 anyOrNull(Bundle.class));
@@ -1786,6 +1788,7 @@
         // This shouldn't have been called.
         verify(mMockActivityTaskManagerInternal, times(0)).startActivitiesAsPackage(
                 anyString(),
+                isNull(),
                 anyInt(),
                 any(Intent[].class),
                 anyOrNull(Bundle.class));
diff --git a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
index ef87e557..3708571 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.CrossProfileAppsInternal;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -37,6 +38,7 @@
 
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
+import com.android.server.LocalServices;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import org.junit.Before;
@@ -57,6 +59,7 @@
 @RunWith(MockitoJUnitRunner.class)
 public class CrossProfileAppsServiceImplTest {
     private static final String PACKAGE_ONE = "com.one";
+    private static final String FEATURE_ID = "feature.one";
     private static final int PACKAGE_ONE_UID = 1111;
     private static final ComponentName ACTIVITY_COMPONENT =
             new ComponentName("com.one", "test");
@@ -97,6 +100,7 @@
     @Before
     public void initCrossProfileAppsServiceImpl() {
         mTestInjector = new TestInjector();
+        LocalServices.removeServiceForTest(CrossProfileAppsInternal.class);
         mCrossProfileAppsServiceImpl = new CrossProfileAppsServiceImpl(mContext, mTestInjector);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
     }
@@ -222,6 +226,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PRIMARY_USER).getIdentifier(),
                                 true));
@@ -230,6 +235,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -243,6 +249,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PRIMARY_USER).getIdentifier(),
                                 false));
@@ -251,6 +258,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -266,6 +274,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 true));
@@ -274,6 +283,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -289,6 +299,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 false));
@@ -297,6 +308,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -310,6 +322,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_TWO,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 true));
@@ -318,6 +331,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -331,6 +345,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_TWO,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 false));
@@ -339,6 +354,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -354,6 +370,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 true));
@@ -362,6 +379,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -382,6 +400,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 false));
@@ -390,6 +409,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -403,6 +423,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 new ComponentName(PACKAGE_TWO, "test"),
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 true));
@@ -411,6 +432,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -424,6 +446,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 new ComponentName(PACKAGE_TWO, "test"),
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 false));
@@ -432,6 +455,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -445,6 +469,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(SECONDARY_USER).getIdentifier(),
                                 true));
@@ -453,6 +478,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -466,6 +492,7 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
+                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(SECONDARY_USER).getIdentifier(),
                                 false));
@@ -474,6 +501,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
+                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -486,6 +514,7 @@
         mCrossProfileAppsServiceImpl.startActivityAsUser(
                 mIApplicationThread,
                 PACKAGE_ONE,
+                FEATURE_ID,
                 ACTIVITY_COMPONENT,
                 UserHandle.of(PRIMARY_USER).getIdentifier(),
                 true);
@@ -494,6 +523,7 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         eq(PACKAGE_ONE),
+                        eq(FEATURE_ID),
                         any(Intent.class),
                         nullable(Bundle.class),
                         eq(PRIMARY_USER));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index f036708..2936bdd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -3144,7 +3144,7 @@
             // Not launchable.
             doReturn(ActivityManager.START_CLASS_NOT_FOUND)
                     .when(mMockActivityTaskManagerInternal).startActivitiesAsPackage(
-                    anyStringOrNull(), anyInt(),
+                    anyStringOrNull(), anyStringOrNull(), anyInt(),
                     anyOrNull(Intent[].class), anyOrNull(Bundle.class));
             assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_0,
                     ActivityNotFoundException.class);
@@ -3153,7 +3153,7 @@
             doReturn(ActivityManager.START_CLASS_NOT_FOUND)
                     .when(mMockActivityTaskManagerInternal)
                     .startActivitiesAsPackage(
-                            anyStringOrNull(), anyInt(),
+                            anyStringOrNull(), anyStringOrNull(), anyInt(),
                             anyOrNull(Intent[].class), anyOrNull(Bundle.class));
             assertStartShortcutThrowsException(CALLING_PACKAGE_1, "s1", USER_0,
                     ActivityNotFoundException.class);
diff --git a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
index 642cedb..c4289ef 100644
--- a/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/AttentionDetectorTest.java
@@ -92,18 +92,18 @@
         mNextDimming = SystemClock.uptimeMillis() + 3000L;
 
         // Save the existing state.
-        mIsSettingEnabled = Settings.System.getIntForUser(getContext().getContentResolver(),
-                Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT);
+        mIsSettingEnabled = Settings.Secure.getIntForUser(getContext().getContentResolver(),
+                Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT);
 
-        Settings.System.putIntForUser(getContext().getContentResolver(),
-                Settings.System.ADAPTIVE_SLEEP, 1, UserHandle.USER_CURRENT);
+        Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                Settings.Secure.ADAPTIVE_SLEEP, 1, UserHandle.USER_CURRENT);
         mAttentionDetector.updateEnabledFromSettings(getContext());
     }
 
     @After
     public void tearDown() {
-        Settings.System.putIntForUser(getContext().getContentResolver(),
-                Settings.System.ADAPTIVE_SLEEP, mIsSettingEnabled, UserHandle.USER_CURRENT);
+        Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                Settings.Secure.ADAPTIVE_SLEEP, mIsSettingEnabled, UserHandle.USER_CURRENT);
 
         DeviceConfig.setProperty(NAMESPACE_ATTENTION_MANAGER_SERVICE,
                 KEY_PRE_DIM_CHECK_DURATION_MILLIS,
@@ -122,8 +122,8 @@
 
     @Test
     public void testOnUserActivity_doesntCheckIfNotEnabled() {
-        Settings.System.putIntForUser(getContext().getContentResolver(),
-                Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT);
+        Settings.Secure.putIntForUser(getContext().getContentResolver(),
+                Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT);
         mAttentionDetector.updateEnabledFromSettings(getContext());
         long when = registerAttention();
         verify(mAttentionManagerInternal, never()).checkAttention(anyLong(), any());
@@ -163,8 +163,8 @@
                 PackageManager.PERMISSION_DENIED);
 
         registerAttention();
-        boolean enabled = Settings.System.getIntForUser(getContext().getContentResolver(),
-                Settings.System.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1;
+        boolean enabled = Settings.Secure.getIntForUser(getContext().getContentResolver(),
+                Settings.Secure.ADAPTIVE_SLEEP, 0, UserHandle.USER_CURRENT) == 1;
         assertFalse(enabled);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 290683d..e768205 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -685,7 +685,30 @@
         reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
-        mInjector.mElapsedRealtime += mInjector.getRestrictedBucketDelayMs() - 5000;
+        mInjector.mElapsedRealtime += mInjector.getAutoRestrictedBucketDelayMs() - 5000;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
+                REASON_MAIN_FORCED_BY_SYSTEM);
+        // Bucket shouldn't change
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // bucketing works after timeout
+        mInjector.mElapsedRealtime += 6000;
+
+        Thread.sleep(6000);
+        // Enough time has passed. The app should automatically be put into the RESTRICTED bucket.
+        assertBucket(STANDBY_BUCKET_RESTRICTED);
+    }
+
+    /**
+     * Test that an app is put into the RESTRICTED bucket after enough time has passed.
+     */
+    @Test
+    public void testRestrictedDelay_DelayChange() throws Exception {
+        reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        mInjector.mAutoRestrictedBucketDelayMs = 2 * HOUR_MS;
+        mInjector.mElapsedRealtime += 2 * HOUR_MS - 5000;
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
                 REASON_MAIN_FORCED_BY_SYSTEM);
         // Bucket shouldn't change
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 57428dc..a0f7f5b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -98,6 +98,7 @@
 import android.app.PendingIntent;
 import android.app.Person;
 import android.app.RemoteInput;
+import android.app.StatsManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.UsageStatsManagerInternal;
 import android.companion.ICompanionDeviceManager;
@@ -267,6 +268,8 @@
     UserManager mUm;
     @Mock
     NotificationHistoryManager mHistoryManager;
+    @Mock
+    StatsManager mStatsManager;
     NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
     private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake(
             1 << 30);
@@ -440,7 +443,7 @@
                 mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
                 mGroupHelper, mAm, mAppUsageStats,
                 mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
-                mAppOpsManager, mUm, mHistoryManager);
+                mAppOpsManager, mUm, mHistoryManager, mStatsManager);
         mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
 
         mService.setAudioManager(mAudioManager);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
new file mode 100644
index 0000000..f051fa4
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotificationRecordLoggerTest extends UiServiceTestCase {
+    private static final int UID = 9999;
+    private static final String CHANNEL_ID = "NotificationRecordLoggerTestChannelId";
+
+    private NotificationRecord getNotification(int id, String tag) {
+        final String packageName = mContext.getPackageName();
+        NotificationChannel channel = new NotificationChannel(
+                CHANNEL_ID, CHANNEL_ID, IMPORTANCE_DEFAULT);
+        Notification.Builder nb = new Notification.Builder(mContext, channel.getId());
+        StatusBarNotification sbn = new StatusBarNotification(packageName, packageName, id, tag,
+                UID, 0, nb.build(), new UserHandle(UID), null,
+                0);
+        return new NotificationRecord(mContext, sbn, channel);
+    }
+
+    private NotificationRecordLogger.NotificationRecordPair getNotificationRecordPair(int id,
+            String tag) {
+        return new NotificationRecordLogger.NotificationRecordPair(getNotification(id, tag),
+                null);
+    }
+
+    @Test
+    public void testSmallHash() {
+        assertEquals(0, NotificationRecordLogger.NotificationRecordPair.smallHash(0));
+        final int maxHash = NotificationRecordLogger.NotificationRecordPair.MAX_HASH;
+        assertEquals(0,
+                NotificationRecordLogger.NotificationRecordPair.smallHash(maxHash));
+        assertEquals(0,
+                NotificationRecordLogger.NotificationRecordPair.smallHash(17 * maxHash));
+        assertEquals(maxHash - 1,
+                NotificationRecordLogger.NotificationRecordPair.smallHash(maxHash - 1));
+        assertEquals(maxHash - 1,
+                NotificationRecordLogger.NotificationRecordPair.smallHash(-1));
+    }
+
+    @Test
+    public void testGetNotificationIdHash() {
+        assertEquals(0,
+                getNotificationRecordPair(0, null).getNotificationIdHash());
+        assertEquals(1,
+                getNotificationRecordPair(1, null).getNotificationIdHash());
+        assertEquals(NotificationRecordLogger.NotificationRecordPair.MAX_HASH - 1,
+                getNotificationRecordPair(-1, null).getNotificationIdHash());
+        final String tag = "someTag";
+        final int hash = NotificationRecordLogger.NotificationRecordPair.smallHash(tag.hashCode());
+        assertEquals(hash, getNotificationRecordPair(0, tag).getNotificationIdHash());
+        // We xor the tag and hashcode together before compressing the range. The order of
+        // operations doesn't matter if id is small.
+        assertEquals(1 ^ hash,
+                getNotificationRecordPair(1, tag).getNotificationIdHash());
+        // But it does matter for an id with more 1 bits than fit in the small hash.
+        assertEquals(
+                NotificationRecordLogger.NotificationRecordPair.smallHash(-1 ^ tag.hashCode()),
+                getNotificationRecordPair(-1, tag).getNotificationIdHash());
+        assertNotEquals(-1 ^ hash,
+                NotificationRecordLogger.NotificationRecordPair.smallHash(-1 ^ tag.hashCode()));
+    }
+
+    @Test
+    public void testGetChannelIdHash() {
+        assertEquals(
+                NotificationRecordLogger.NotificationRecordPair.smallHash(CHANNEL_ID.hashCode()),
+                getNotificationRecordPair(0, null).getChannelIdHash());
+        assertNotEquals(
+                NotificationRecordLogger.NotificationRecordPair.smallHash(CHANNEL_ID.hashCode()),
+                CHANNEL_ID.hashCode());
+    }
+
+    @Test
+    public void testGetGroupIdHash() {
+        NotificationRecordLogger.NotificationRecordPair p = getNotificationRecordPair(
+                0, null);
+        assertEquals(0, p.getGroupIdHash());
+        final String group = "someGroup";
+        p.r.setOverrideGroupKey(group);
+        assertEquals(
+                NotificationRecordLogger.NotificationRecordPair.smallHash(group.hashCode()),
+                p.getGroupIdHash());
+    }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index 64a9804..27f72a1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -35,6 +35,7 @@
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.IUriGrantsManager;
+import android.app.StatsManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.role.RoleManager;
 import android.app.usage.UsageStatsManagerInternal;
@@ -142,7 +143,8 @@
                     mock(UsageStatsManagerInternal.class),
                     mock(DevicePolicyManagerInternal.class), mock(IUriGrantsManager.class),
                     mock(UriGrantsManagerInternal.class),
-                    mock(AppOpsManager.class), mUm, mock(NotificationHistoryManager.class));
+                    mock(AppOpsManager.class), mUm, mock(NotificationHistoryManager.class),
+                    mock(StatsManager.class));
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 135d005..af04f76 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -120,7 +120,7 @@
         mInterceptor = new ActivityStartInterceptor(
                 mService, mSupervisor, mRootWindowContainer, mContext);
         mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID,
-                TEST_START_FLAGS, TEST_CALLING_PACKAGE);
+                TEST_START_FLAGS, TEST_CALLING_PACKAGE, null);
 
         // Mock ActivityManagerInternal
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index bab877e..fa182d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -275,7 +275,7 @@
 
         if (containsConditions(preconditions, PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
             doReturn(false).when(service.mStackSupervisor).checkStartAnyActivityPermission(
-                    any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
+                    any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), any(),
                     anyBoolean(), anyBoolean(), any(), any(), any());
         }
 
@@ -349,7 +349,7 @@
             boolean mockGetLaunchStack) {
         // always allow test to start activity.
         doReturn(true).when(mSupervisor).checkStartAnyActivityPermission(
-                any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
+                any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), any(),
                 anyBoolean(), anyBoolean(), any(), any(), any());
 
         if (mockGetLaunchStack) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index eb84d0a..c93dc97 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -255,10 +255,10 @@
 
             final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
                     mLaunchedFromPid /* launchedFromPid */, mLaunchedFromUid /* launchedFromUid */,
-                    null, intent, null, aInfo /*aInfo*/, new Configuration(), null /* resultTo */,
-                    null /* resultWho */, 0 /* reqCode */, false /*componentSpecified*/,
-                    false /* rootVoiceInteraction */, mService.mStackSupervisor, options,
-                    null /* sourceRecord */);
+                    null, null, intent, null, aInfo /*aInfo*/, new Configuration(),
+                    null /* resultTo */, null /* resultWho */, 0 /* reqCode */,
+                    false /*componentSpecified*/, false /* rootVoiceInteraction */,
+                    mService.mStackSupervisor, options, null /* sourceRecord */);
             spyOn(activity);
             if (mTask != null) {
                 // fullscreen value is normally read from resources in ctor, so for testing we need
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index f754c59..39cd76a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -174,6 +174,6 @@
     }
 
     private static DisplayInfo displayInfoForRotation(int rotation, boolean withDisplayCutout) {
-        return displayInfoAndCutoutForRotation(rotation, withDisplayCutout).first;
+        return displayInfoAndCutoutForRotation(rotation, withDisplayCutout, false).first;
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index e6291a4..c19312d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -34,6 +34,7 @@
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
@@ -91,6 +92,7 @@
     private WindowState mWindow;
     private int mRotation = ROTATION_0;
     private boolean mHasDisplayCutout;
+    private boolean mIsLongEdgeDisplayCutout;
     private static final int DECOR_WINDOW_INSET = 50;
 
     @Before
@@ -124,6 +126,12 @@
         updateDisplayFrames();
     }
 
+    public void addLongEdgeDisplayCutout() {
+        mHasDisplayCutout = true;
+        mIsLongEdgeDisplayCutout = true;
+        updateDisplayFrames();
+    }
+
     private void updateDisplayFrames() {
         mFrames = createDisplayFrames();
         mDisplayContent.mDisplayFrames = mFrames;
@@ -131,7 +139,7 @@
 
     private DisplayFrames createDisplayFrames() {
         final Pair<DisplayInfo, WmDisplayCutout> info = displayInfoAndCutoutForRotation(mRotation,
-                mHasDisplayCutout);
+                mHasDisplayCutout, mIsLongEdgeDisplayCutout);
         return new DisplayFrames(mDisplayContent.getDisplayId(), info.first, info.second);
     }
 
@@ -376,6 +384,46 @@
     }
 
     @Test
+    public void layoutWindowLw_withDisplayCutout_shortEdges() {
+        addDisplayCutout();
+
+        mWindow.mAttrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
+        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withDisplayCutout_always() {
+        addDisplayCutout();
+
+        mWindow.mAttrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
+        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+    }
+
+    @Test
     public void layoutWindowLw_withDisplayCutout_layoutFullscreen() {
         addDisplayCutout();
 
@@ -551,6 +599,88 @@
     }
 
     @Test
+    public void layoutWindowLw_withLongEdgeDisplayCutout() {
+        addLongEdgeDisplayCutout();
+
+        mWindow.mAttrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
+                NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withLongEdgeDisplayCutout_never() {
+        addLongEdgeDisplayCutout();
+
+        mWindow.mAttrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0,
+                NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
+                NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withLongEdgeDisplayCutout_shortEdges() {
+        addLongEdgeDisplayCutout();
+
+        mWindow.mAttrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
+        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
+                NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+    }
+
+    @Test
+    public void layoutWindowLw_withLongEdgeDisplayCutout_always() {
+        addLongEdgeDisplayCutout();
+
+        mWindow.mAttrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
+        mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        addWindow(mWindow);
+
+        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
+
+        assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
+                NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+    }
+
+    @Test
     public void layoutWindowLw_withForwardInset_SoftInputAdjustResize() {
         assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_NONE);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index d0b3350..2a20c2e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -111,7 +111,7 @@
     }
 
     static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation,
-            boolean withDisplayCutout) {
+            boolean withDisplayCutout, boolean isLongEdgeCutout) {
         final DisplayInfo info = new DisplayInfo();
         WmDisplayCutout cutout = null;
 
@@ -121,7 +121,7 @@
         info.rotation = rotation;
         if (withDisplayCutout) {
             cutout = WmDisplayCutout.computeSafeInsets(
-                    displayCutoutForRotation(rotation), info.logicalWidth,
+                    displayCutoutForRotation(rotation, isLongEdgeCutout), info.logicalWidth,
                     info.logicalHeight);
             info.displayCutout = cutout.getDisplayCutout();
         } else {
@@ -130,9 +130,13 @@
         return Pair.create(info, cutout);
     }
 
-    private static DisplayCutout displayCutoutForRotation(int rotation) {
-        final RectF rectF =
-                new RectF(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT);
+    private static DisplayCutout displayCutoutForRotation(int rotation, boolean isLongEdgeCutout) {
+        RectF rectF = new RectF();
+        if (isLongEdgeCutout) {
+            rectF.set(0, DISPLAY_HEIGHT / 4, DISPLAY_CUTOUT_HEIGHT, DISPLAY_HEIGHT * 3 / 4);
+        } else {
+            rectF.set(DISPLAY_WIDTH / 4, 0, DISPLAY_WIDTH * 3 / 4, DISPLAY_CUTOUT_HEIGHT);
+        }
 
         final Matrix m = new Matrix();
         transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m);
@@ -141,16 +145,16 @@
         int pos = -1;
         switch (rotation) {
             case ROTATION_0:
-                pos = BOUNDS_POSITION_TOP;
+                pos = isLongEdgeCutout ? BOUNDS_POSITION_LEFT : BOUNDS_POSITION_TOP;
                 break;
             case ROTATION_90:
-                pos = BOUNDS_POSITION_LEFT;
+                pos = isLongEdgeCutout ? BOUNDS_POSITION_BOTTOM : BOUNDS_POSITION_LEFT;
                 break;
             case ROTATION_180:
-                pos = BOUNDS_POSITION_BOTTOM;
+                pos = isLongEdgeCutout ? BOUNDS_POSITION_RIGHT : BOUNDS_POSITION_BOTTOM;
                 break;
             case ROTATION_270:
-                pos = BOUNDS_POSITION_RIGHT;
+                pos = isLongEdgeCutout ? BOUNDS_POSITION_TOP : BOUNDS_POSITION_RIGHT;
                 break;
         }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 7ffdd7c..f811757 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -112,11 +112,44 @@
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
         statusBar.getFrameLw().set(0, 0, 500, 100);
+
+        // We must not have control or control target before we have the insets source window.
+        mProvider.updateControlForTarget(target, true /* force */);
+        assertNull(mProvider.getControl(target));
+        assertNull(mProvider.getControlTarget());
+
+        // We can have the control or the control target after we have the insets source window.
         mProvider.setWindow(statusBar, null, null);
         mProvider.updateControlForTarget(target, false /* force */);
         assertNotNull(mProvider.getControl(target));
+        assertNotNull(mProvider.getControlTarget());
+
+        // We must not have control or control target while we are performing seamless rotation.
+        // And the control and the control target must not be updated during that.
+        mProvider.startSeamlessRotation();
+        assertNull(mProvider.getControl(target));
+        assertNull(mProvider.getControlTarget());
+        mProvider.updateControlForTarget(target, true /* force */);
+        assertNull(mProvider.getControl(target));
+        assertNull(mProvider.getControlTarget());
+
+        // We can have the control and the control target after seamless rotation.
+        mProvider.finishSeamlessRotation(false /* timeout */);
+        mProvider.updateControlForTarget(target, false /* force */);
+        assertNotNull(mProvider.getControl(target));
+        assertNotNull(mProvider.getControlTarget());
+
+        // We can clear the control and the control target.
         mProvider.updateControlForTarget(null, false /* force */);
         assertNull(mProvider.getControl(target));
+        assertNull(mProvider.getControlTarget());
+
+        // We must not have control or control target if the insets source window doesn't have a
+        // surface.
+        statusBar.setSurfaceControl(null);
+        mProvider.updateControlForTarget(target, true /* force */);
+        assertNull(mProvider.getControl(target));
+        assertNull(mProvider.getControlTarget());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index e507508..f517881 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -439,11 +439,11 @@
             doNothing().when(this).updateCpuStats();
 
             // AppOpsService
-            final AppOpsService aos = mock(AppOpsService.class);
-            doReturn(aos).when(this).getAppOpsService();
+            final AppOpsManager aos = mock(AppOpsManager.class);
+            doReturn(aos).when(this).getAppOpsManager();
             // Make sure permission checks aren't overridden.
-            doReturn(AppOpsManager.MODE_DEFAULT).when(aos).noteOperation(anyInt(), anyInt(),
-                    anyString(), nullable(String.class), anyBoolean(), nullable(String.class));
+            doReturn(AppOpsManager.MODE_DEFAULT).when(aos).noteOpNoThrow(anyInt(), anyInt(),
+                    anyString(), nullable(String.class), nullable(String.class));
 
             // UserManagerService
             final UserManagerService ums = mock(UserManagerService.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index ebf14d2..feadd0a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -988,7 +988,7 @@
     private Task createTask(int taskId) {
         return new ActivityStack(mService, taskId, new Intent(), null, null, null,
                 ActivityBuilder.getDefaultComponent(), null, false, false, false, 0, 10050, null,
-                0, false, null, 0, 0, 0, 0, 0, null, 0, false, false, false, 0,
+                0, false, null, 0, 0, 0, 0, 0, null, null, 0, false, false, false, 0,
                 0, null /*ActivityInfo*/, null /*_voiceSession*/, null /*_voiceInteractor*/,
                 null /*stack*/);
     }
@@ -1022,7 +1022,7 @@
                 boolean neverRelinquishIdentity,
                 ActivityManager.TaskDescription lastTaskDescription,
                 int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor,
-                int callingUid, String callingPackage, int resizeMode,
+                int callingUid, String callingPackage, String callingFeatureId, int resizeMode,
                 boolean supportsPictureInPicture,
                 boolean realActivitySuspended, boolean userSetupComplete, int minWidth,
                 int minHeight, ActivityStack stack) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 48b6e2a..1371481 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1132,7 +1132,11 @@
                             // dump everything for all users
                             final int numUsers = mUserState.size();
                             for (int user = 0; user < numUsers; user++) {
-                                ipw.println("user=" + mUserState.keyAt(user));
+                                final int userId = mUserState.keyAt(user);
+                                if (!mUserUnlockedStates.get(userId)) {
+                                    continue;
+                                }
+                                ipw.println("user=" + userId);
                                 ipw.increaseIndent();
                                 mUserState.valueAt(user).dumpFile(ipw, null);
                                 ipw.decreaseIndent();
@@ -1153,7 +1157,11 @@
                             // dump info for all users
                             final int numUsers = mUserState.size();
                             for (int user = 0; user < numUsers; user++) {
-                                ipw.println("user=" + mUserState.keyAt(user));
+                                final int userId = mUserState.keyAt(user);
+                                if (!mUserUnlockedStates.get(userId)) {
+                                    continue;
+                                }
+                                ipw.println("user=" + userId);
                                 ipw.increaseIndent();
                                 mUserState.valueAt(user).dumpDatabaseInfo(ipw);
                                 ipw.decreaseIndent();
@@ -1198,11 +1206,13 @@
                 idpw.printPair("user", userId);
                 idpw.println();
                 idpw.increaseIndent();
-                if (checkin) {
-                    mUserState.valueAt(i).checkin(idpw);
-                } else {
-                    mUserState.valueAt(i).dump(idpw, pkg, compact);
-                    idpw.println();
+                if (mUserUnlockedStates.get(userId)) {
+                    if (checkin) {
+                        mUserState.valueAt(i).checkin(idpw);
+                    } else {
+                        mUserState.valueAt(i).dump(idpw, pkg, compact);
+                        idpw.println();
+                    }
                 }
                 mAppStandby.dumpUser(idpw, userId, pkg);
                 idpw.decreaseIndent();
@@ -1224,7 +1234,7 @@
     private int parseUserIdFromArgs(String[] args, int index, IndentingPrintWriter ipw) {
         final int userId;
         try {
-            userId = Integer.valueOf(args[index + 1]);
+            userId = Integer.parseInt(args[index + 1]);
         } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
             ipw.println("invalid user specified.");
             return UserHandle.USER_NULL;
@@ -1233,6 +1243,10 @@
             ipw.println("the specified user does not exist.");
             return UserHandle.USER_NULL;
         }
+        if (!mUserUnlockedStates.get(userId)) {
+            ipw.println("the specified user is currently in a locked state.");
+            return UserHandle.USER_NULL;
+        }
         return userId;
     }
 
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index d5eec33..8378d8e 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -747,7 +747,8 @@
         }
 
         @Override
-        public int startVoiceActivity(IBinder token, Intent intent, String resolvedType) {
+        public int startVoiceActivity(IBinder token, Intent intent, String resolvedType,
+                String callingFeatureId) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "startVoiceActivity without running voice interaction service");
@@ -757,8 +758,8 @@
                 final int callingUid = Binder.getCallingUid();
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    return mImpl.startVoiceActivityLocked(callingPid, callingUid, token,
-                            intent, resolvedType);
+                    return mImpl.startVoiceActivityLocked(callingFeatureId, callingPid, callingUid,
+                            token, intent, resolvedType);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -766,7 +767,8 @@
         }
 
         @Override
-        public int startAssistantActivity(IBinder token, Intent intent, String resolvedType) {
+        public int startAssistantActivity(IBinder token, Intent intent, String resolvedType,
+                String callingFeatureId) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "startAssistantActivity without running voice interaction service");
@@ -776,8 +778,8 @@
                 final int callingUid = Binder.getCallingUid();
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    return mImpl.startAssistantActivityLocked(callingPid, callingUid, token,
-                            intent, resolvedType);
+                    return mImpl.startAssistantActivityLocked(callingFeatureId, callingPid,
+                            callingUid, token, intent, resolvedType);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index a1210cf..a62b03c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -216,8 +216,8 @@
         return true;
     }
 
-    public int startVoiceActivityLocked(int callingPid, int callingUid, IBinder token,
-            Intent intent, String resolvedType) {
+    public int startVoiceActivityLocked(@Nullable String callingFeatureId, int callingPid,
+            int callingUid, IBinder token, Intent intent, String resolvedType) {
         try {
             if (mActiveSession == null || token != mActiveSession.mToken) {
                 Slog.w(TAG, "startVoiceActivity does not match active session");
@@ -230,16 +230,16 @@
             intent = new Intent(intent);
             intent.addCategory(Intent.CATEGORY_VOICE);
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
-            return mAtm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid,
-                    intent, resolvedType, mActiveSession.mSession, mActiveSession.mInteractor,
-                    0, null, null, mUser);
+            return mAtm.startVoiceActivity(mComponent.getPackageName(), callingFeatureId,
+                    callingPid, callingUid, intent, resolvedType, mActiveSession.mSession,
+                    mActiveSession.mInteractor, 0, null, null, mUser);
         } catch (RemoteException e) {
             throw new IllegalStateException("Unexpected remote error", e);
         }
     }
 
-    public int startAssistantActivityLocked(int callingPid, int callingUid, IBinder token,
-            Intent intent, String resolvedType) {
+    public int startAssistantActivityLocked(@Nullable String callingFeatureId, int callingPid,
+            int callingUid, IBinder token, Intent intent, String resolvedType) {
         try {
             if (mActiveSession == null || token != mActiveSession.mToken) {
                 Slog.w(TAG, "startAssistantActivity does not match active session");
@@ -253,8 +253,8 @@
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             final ActivityOptions options = ActivityOptions.makeBasic();
             options.setLaunchActivityType(ACTIVITY_TYPE_ASSISTANT);
-            return mAtm.startAssistantActivity(mComponent.getPackageName(), callingPid, callingUid,
-                    intent, resolvedType, options.toBundle(), mUser);
+            return mAtm.startAssistantActivity(mComponent.getPackageName(), callingFeatureId,
+                    callingPid, callingUid, intent, resolvedType, options.toBundle(), mUser);
         } catch (RemoteException e) {
             throw new IllegalStateException("Unexpected remote error", e);
         }
diff --git a/startop/iorap/stress/Android.bp b/startop/iorap/stress/Android.bp
new file mode 100644
index 0000000..f9f251bd
--- /dev/null
+++ b/startop/iorap/stress/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+  name: "iorap.stress.memory",
+  srcs: ["main_memory.cc"],
+
+  cflags: [
+      "-Wall",
+      "-Wextra",
+      "-Werror",
+      "-Wno-unused-parameter"
+  ],
+
+  shared_libs: [
+      "libbase"
+  ],
+
+  host_supported: true,
+}
diff --git a/startop/iorap/stress/main_memory.cc b/startop/iorap/stress/main_memory.cc
new file mode 100644
index 0000000..1f26861
--- /dev/null
+++ b/startop/iorap/stress/main_memory.cc
@@ -0,0 +1,126 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include <chrono>
+#include <fstream>
+#include <iostream>
+#include <random>
+#include <string>
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+
+#include <android-base/parseint.h>
+
+static constexpr size_t kBytesPerMb = 1048576;
+const size_t kMemoryAllocationSize = 2 * 1024 * kBytesPerMb;
+
+#define USE_MLOCKALL 0
+
+std::string GetProcessStatus(const char* key) {
+  // Build search pattern of key and separator.
+  std::string pattern(key);
+  pattern.push_back(':');
+
+  // Search for status lines starting with pattern.
+  std::ifstream fs("/proc/self/status");
+  std::string line;
+  while (std::getline(fs, line)) {
+    if (strncmp(pattern.c_str(), line.c_str(), pattern.size()) == 0) {
+      // Skip whitespace in matching line (if any).
+      size_t pos = line.find_first_not_of(" \t", pattern.size());
+      if (pos == std::string::npos) {
+        break;
+      }
+      return std::string(line, pos);
+    }
+  }
+  return "<unknown>";
+}
+
+int main(int argc, char** argv) {
+  size_t allocationSize = 0;
+  if (argc >= 2) {
+    if (!android::base::ParseUint(argv[1], /*out*/&allocationSize)) {
+      std::cerr << "Failed to parse the allocation size (must be 0,MAX_SIZE_T)" << std::endl;
+      return 1;
+    }
+  } else {
+    allocationSize = kMemoryAllocationSize;
+  }
+
+  void* mem = malloc(allocationSize);
+  if (mem == nullptr) {
+    std::cerr << "Malloc failed" << std::endl;
+    return 1;
+  }
+
+  volatile int* imem = static_cast<int *>(mem);  // don't optimize out memory usage
+
+  size_t imemCount = allocationSize / sizeof(int);
+
+  std::cout << "Allocated " << allocationSize << " bytes" << std::endl;
+
+  auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
+  std::mt19937 mt_rand(seed);
+
+  size_t randPrintCount = 10;
+
+  // Write random numbers:
+  // * Ensures each page is resident
+  // * Avoids zeroed out pages (zRAM)
+  // * Avoids same-page merging
+  for (size_t i = 0; i < imemCount; ++i) {
+    imem[i] = mt_rand();
+
+    if (i < randPrintCount) {
+      std::cout << "Generated random value: " << imem[i] << std::endl;
+    }
+  }
+
+#if USE_MLOCKALL
+  /*
+   * Lock all pages from the address space of this process.
+   */
+  if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
+    std::cerr << "Mlockall failed" << std::endl;
+    return 1;
+  }
+#else
+  // Use mlock because of the predictable VmLck size.
+  // Using mlockall tends to bring in anywhere from 2-2.5GB depending on the device.
+  if (mlock(mem, allocationSize) != 0) {
+    std::cerr << "Mlock failed" << std::endl;
+    return 1;
+  }
+#endif
+
+  // Validate memory is actually resident and locked with:
+  // $> cat /proc/$(pidof iorap.stress.memory)/status | grep VmLck
+  std::cout << "Locked memory (VmLck) = " << GetProcessStatus("VmLck") << std::endl;
+
+  std::cout << "Press any key to terminate" << std::endl;
+  int any_input;
+  std::cin >> any_input;
+
+  std::cout << "Terminating..." << std::endl;
+
+  munlockall();
+  free(mem);
+
+  return 0;
+}
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
index 460add8..18c2491 100644
--- a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
@@ -16,6 +16,7 @@
 
 import android.net.Uri
 import android.os.ServiceManager
+import androidx.test.filters.FlakyTest
 import androidx.test.filters.MediumTest
 import org.junit.Test
 import org.mockito.Mockito.argThat
@@ -26,6 +27,7 @@
 
 // @Ignore("Test is disabled until iorapd is added to init and there's selinux policies for it")
 @MediumTest
+@FlakyTest(bugId = 149098310) // Failing on cuttlefish with SecurityException.
 class IIorapIntegrationTest {
     /**
      * @throws ServiceManager.ServiceNotFoundException if iorapd service could not be found
@@ -55,6 +57,9 @@
     private fun testAnyMethod(func: (RequestId) -> Unit) {
         val taskListener = spy(DummyTaskListener())!!
 
+        // FIXME: b/149098310
+        return
+
         try {
             iorapService.setTaskListener(taskListener)
             // Note: Binder guarantees total order for oneway messages sent to the same binder
diff --git a/startop/view_compiler/dex_layout_compiler.cc b/startop/view_compiler/dex_layout_compiler.cc
index cb820f8..bddb8aa 100644
--- a/startop/view_compiler/dex_layout_compiler.cc
+++ b/startop/view_compiler/dex_layout_compiler.cc
@@ -118,7 +118,7 @@
 std::string ResolveName(const std::string& name) {
   if (name == "View") return "android.view.View";
   if (name == "ViewGroup") return "android.view.ViewGroup";
-  if (name.find(".") == std::string::npos) {
+  if (name.find('.') == std::string::npos) {
     return StringPrintf("android.widget.%s", name.c_str());
   }
   return name;
@@ -205,4 +205,4 @@
   view_stack_.pop_back();
 }
 
-}  // namespace startop
\ No newline at end of file
+}  // namespace startop
diff --git a/startop/view_compiler/util.cc b/startop/view_compiler/util.cc
index a0637e6..c34d7b0 100644
--- a/startop/view_compiler/util.cc
+++ b/startop/view_compiler/util.cc
@@ -23,13 +23,13 @@
 
 // TODO: see if we can borrow this from somewhere else, like aapt2.
 string FindLayoutNameFromFilename(const string& filename) {
-  size_t start = filename.rfind("/");
+  size_t start = filename.rfind('/');
   if (start == string::npos) {
     start = 0;
   } else {
     start++;  // advance past '/' character
   }
-  size_t end = filename.find(".", start);
+  size_t end = filename.find('.', start);
 
   return filename.substr(start, end - start);
 }
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 6d82e51..37d3d32 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -627,7 +627,7 @@
                     destinationAddress, message, statusReportRequested);
         }
 
-        return new SubmitPdu(spb);
+        return spb != null ? new SubmitPdu(spb) : null;
     }
 
     /**
@@ -655,7 +655,7 @@
                     destinationAddress, destinationPort, data, statusReportRequested);
         }
 
-        return new SubmitPdu(spb);
+        return spb != null ? new SubmitPdu(spb) : null;
     }
 
     // TODO: SubmitPdu class is used for SMS-DELIVER also now. Refactor for SubmitPdu and new
diff --git a/core/java/com/android/internal/telephony/IWapPushManager.aidl b/telephony/java/com/android/internal/telephony/IWapPushManager.aidl
similarity index 82%
rename from core/java/com/android/internal/telephony/IWapPushManager.aidl
rename to telephony/java/com/android/internal/telephony/IWapPushManager.aidl
index 9f6851b..1c3df6533 100644
--- a/core/java/com/android/internal/telephony/IWapPushManager.aidl
+++ b/telephony/java/com/android/internal/telephony/IWapPushManager.aidl
@@ -18,7 +18,6 @@
 
 import android.content.Intent;
 
-/** @hide */
 interface IWapPushManager {
     /**
      * Processes WAP push message and triggers the receiver application registered
@@ -27,10 +26,11 @@
     int processMessage(String app_id, String content_type, in Intent intent);
 
     /**
-     * Adds receiver application into the application ID table.
-     * Returns true if inserting the information is successful. Inserting duplicated
+     * Add receiver application into the application ID table.
+     * Returns true if inserting the information is successfull. Inserting the duplicated
      * record in the application ID table is not allowed. Use update/delete method.
      */
+    @UnsupportedAppUsage
     boolean addPackage(String x_app_id, String content_type,
             String package_name, String class_name,
             int app_type, boolean need_signature, boolean further_processing);
@@ -39,14 +39,17 @@
      * Updates receiver application that is last added.
      * Returns true if updating the information is successfull.
      */
+    @UnsupportedAppUsage
     boolean updatePackage(String x_app_id, String content_type,
             String package_name, String class_name,
             int app_type, boolean need_signature, boolean further_processing);
 
     /**
-     * Deletes receiver application information.
+     * Delites receiver application information.
      * Returns true if deleting is successfull.
      */
+    @UnsupportedAppUsage
     boolean deletePackage(String x_app_id, String content_type,
-            String package_name, String class_name);
+                            String package_name, String class_name);
 }
+
diff --git a/tests/BootImageProfileTest/AndroidTest.xml b/tests/BootImageProfileTest/AndroidTest.xml
index b4f2663..d7f8204 100644
--- a/tests/BootImageProfileTest/AndroidTest.xml
+++ b/tests/BootImageProfileTest/AndroidTest.xml
@@ -19,18 +19,8 @@
     -->
     <target_preparer
         class="com.android.tradefed.targetprep.DeviceSetup">
-        <!-- we need this magic flag, otherwise it always reboots and breaks the selinux -->
+        <!-- we need this magic flag, otherwise it always reboots and breaks selinux -->
         <option name="force-skip-system-props" value="true" />
-
-        <option name="run-command" value="device_config put runtime_native_boot profilesystemserver true" />
-        <option name="run-command" value="device_config put runtime_native_boot profilebootclasspath true" />
-
-        <!-- Profiling does not pick up the above changes we restart the shell -->
-        <option name="run-command" value="stop" />
-        <option name="run-command" value="start" />
-
-        <!-- give it some time to restart the shell; otherwise the first unit test might fail -->
-        <option name="run-command" value="sleep 2" />
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.HostTest" >
diff --git a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
index 10f3e54..1c8b6be 100644
--- a/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
+++ b/tests/BootImageProfileTest/src/com/android/bootimageprofile/BootImageProfileTest.java
@@ -42,10 +42,9 @@
     }
 
     /**
-     * Test that the boot image profile properties are set.
+     * Validate that the boot image profile properties are set.
      */
-    @Test
-    public void testProperties() throws Exception {
+    public void validateProperties() throws Exception {
         String res = mTestDevice.getProperty(
                 "persist.device_config.runtime_native_boot.profilebootclasspath");
         assertTrue("profile boot class path not enabled", res != null && res.equals("true"));
@@ -67,13 +66,37 @@
 
     @Test
     public void testSystemServerProfile() throws Exception {
+        final int numIterations = 20;
+        for (int i = 1; i <= numIterations; ++i) {
+            String res;
+            res = mTestDevice.getProperty(
+                    "persist.device_config.runtime_native_boot.profilebootclasspath");
+            boolean profileBootClassPath = res != null && res.equals("true");
+            res = mTestDevice.getProperty(
+                    "persist.device_config.runtime_native_boot.profilesystemserver");
+            boolean profileSystemServer = res != null && res.equals("true");
+            if (profileBootClassPath && profileSystemServer) {
+                break;
+            }
+            if (i == numIterations) {
+                assertTrue("profile system server not enabled", profileSystemServer);
+                assertTrue("profile boot class path not enabled", profileSystemServer);
+            }
+
+            res = mTestDevice.executeShellCommand(
+                    "device_config put runtime_native_boot profilebootclasspath true");
+            res = mTestDevice.executeShellCommand(
+                    "device_config put runtime_native_boot profilesystemserver true");
+            res = mTestDevice.executeShellCommand("stop");
+            res = mTestDevice.executeShellCommand("start");
+            Thread.sleep(5000);
+        }
         // Trunacte the profile before force it to be saved to prevent previous profiles
         // causing the test to pass.
         String res;
         res = mTestDevice.executeShellCommand("truncate -s 0 " + SYSTEM_SERVER_PROFILE).trim();
         assertTrue(res, res.length() == 0);
         // Wait up to 20 seconds for the profile to be saved.
-        final int numIterations = 20;
         for (int i = 1; i <= numIterations; ++i) {
             // Force save the profile since we truncated it.
             if (forceSaveProfile("system_server")) {
@@ -89,6 +112,9 @@
             // In case the profile is partially saved, wait an extra second.
             Thread.sleep(1000);
 
+            // Validate that properties are still set.
+            validateProperties();
+
             // Validate that the profile is non empty.
             res = mTestDevice.executeShellCommand("profman --dump-only --profile-file="
                     + SYSTEM_SERVER_PROFILE);
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
index 1361df3..5904916 100644
--- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
@@ -321,7 +321,8 @@
                 }
 
                 mAtm.startActivityAndWait(null,
-                        getInstrumentation().getContext().getBasePackageName(), mLaunchIntent,
+                        getInstrumentation().getContext().getBasePackageName(),
+                        getInstrumentation().getContext().getFeatureId(), mLaunchIntent,
                         mimeType, null, null, 0, mLaunchIntent.getFlags(), null, null,
                         UserHandle.USER_CURRENT_OR_SELF);
             } catch (RemoteException e) {
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
index 0393248..a72bd38 100644
--- a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
+++ b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
@@ -16,6 +16,7 @@
 
 package com.android.tests.rollback.host;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.testng.Assert.assertThrows;
 
@@ -115,6 +116,31 @@
      */
     @Test
     public void testNetworkPassedDoesNotRollback() throws Exception {
+        runPhase("testNetworkPassedDoesNotRollback_Phase1");
+        // Reboot device to activate staged package
+        getDevice().reboot();
+
+        // Verify rollback was enabled
+        runPhase("testNetworkPassedDoesNotRollback_Phase2");
+
+        // Connect to internet so network health check passes
+        getDevice().executeShellCommand("svc wifi enable");
+        getDevice().executeShellCommand("svc data enable");
+
+        // Wait for device available because emulator device may restart after turning
+        // on mobile data
+        getDevice().waitForDeviceAvailable();
+
+        // Verify rollback was not executed after health check deadline
+        runPhase("testNetworkPassedDoesNotRollback_Phase3");
+        InputStreamSource logcatStream = mReceiver.getLogcatData();
+        try {
+            List<String> watchdogEvents = getWatchdogLoggingEvents(logcatStream);
+            assertEquals(watchdogEventOccurred(watchdogEvents, null, null,
+                    REASON_EXPLICIT_HEALTH_CHECK, null), false);
+        } finally {
+            logcatStream.close();
+        }
     }
 
     /**
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
index e5c8a68..35bc65a 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
@@ -150,6 +150,45 @@
         runShellCommand("pm uninstall " + getNetworkStackPackageName());
     }
 
+    @Test
+    public void testNetworkPassedDoesNotRollback_Phase1() throws Exception {
+        // Remove available rollbacks and uninstall NetworkStack on /data/
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        String networkStack = getNetworkStackPackageName();
+
+        rm.expireRollbackForPackage(networkStack);
+        uninstallNetworkStackPackage();
+
+        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+                networkStack)).isNull();
+
+        // Reduce health check deadline, here unlike the network failed case, we use
+        // a longer deadline because joining a network can take a much longer time for
+        // reasons external to the device than 'not joining'
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+                PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS,
+                Integer.toString(300000), false);
+        // Simulate re-installation of new NetworkStack with rollbacks enabled
+        installNetworkStackPackage();
+    }
+
+    @Test
+    public void testNetworkPassedDoesNotRollback_Phase2() throws Exception {
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+                getNetworkStackPackageName())).isNotNull();
+    }
+
+    @Test
+    public void testNetworkPassedDoesNotRollback_Phase3() throws Exception {
+        // Sleep for > health check deadline. We expect no rollback should happen during sleeping.
+        // If the device reboots for rollback, this device test will fail as well as the host test.
+        Thread.sleep(TimeUnit.SECONDS.toMillis(310));
+        RollbackManager rm = RollbackUtils.getRollbackManager();
+        assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+                getNetworkStackPackageName())).isNull();
+    }
+
     private static void runShellCommand(String cmd) {
         ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .executeShellCommand(cmd);
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index bddd93c..70be83f 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -22,14 +22,11 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.Manifest;
-import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
-import android.os.ParcelFileDescriptor;
 import android.os.storage.StorageManager;
 import android.provider.DeviceConfig;
 
@@ -44,8 +41,6 @@
 import com.android.cts.rollback.lib.RollbackUtils;
 import com.android.internal.R;
 
-import libcore.io.IoUtils;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -65,24 +60,8 @@
  */
 @RunWith(JUnit4.class)
 public class StagedRollbackTest {
-
-    private static final String NETWORK_STACK_CONNECTOR_CLASS =
-            "android.net.INetworkStackConnector";
     private static final String PROPERTY_WATCHDOG_TRIGGER_FAILURE_COUNT =
             "watchdog_trigger_failure_count";
-    private static final String PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS =
-            "watchdog_request_timeout_millis";
-
-    private static final TestApp NETWORK_STACK = new TestApp("NetworkStack",
-            getNetworkStackPackageName(), -1, false, findNetworkStackApk());
-
-    private static File findNetworkStackApk() {
-        final File apk = new File("/system/priv-app/NetworkStack/NetworkStack.apk");
-        if (apk.isFile()) {
-            return apk;
-        }
-        return new File("/system/priv-app/NetworkStackNext/NetworkStackNext.apk");
-    }
 
     /**
      * Adopts common shell permissions needed for rollback tests.
@@ -274,23 +253,6 @@
                 TestApp.B)).isNotNull();
     }
 
-    private static String getNetworkStackPackageName() {
-        Intent intent = new Intent(NETWORK_STACK_CONNECTOR_CLASS);
-        ComponentName comp = intent.resolveSystemService(
-                InstrumentationRegistry.getInstrumentation().getContext().getPackageManager(), 0);
-        return comp.getPackageName();
-    }
-
-    private static void installNetworkStackPackage() throws Exception {
-        Install.single(NETWORK_STACK).setStaged().setEnableRollback()
-                .addInstallFlags(PackageManager.INSTALL_REPLACE_EXISTING).commit();
-    }
-
-    private static void uninstallNetworkStackPackage() {
-        // Uninstall the package as a privileged user so we won't fail due to permission.
-        runShellCommand("pm uninstall " + getNetworkStackPackageName());
-    }
-
     @Test
     public void testPreviouslyAbandonedRollbacks_Phase1() throws Exception {
         Uninstall.packages(TestApp.A);
@@ -327,45 +289,6 @@
         Uninstall.packages(TestApp.A);
     }
 
-    @Test
-    public void testNetworkPassedDoesNotRollback_Phase1() throws Exception {
-        // Remove available rollbacks and uninstall NetworkStack on /data/
-        RollbackManager rm = RollbackUtils.getRollbackManager();
-        String networkStack = getNetworkStackPackageName();
-
-        rm.expireRollbackForPackage(networkStack);
-        uninstallNetworkStackPackage();
-
-        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
-                        networkStack)).isNull();
-
-        // Reduce health check deadline, here unlike the network failed case, we use
-        // a longer deadline because joining a network can take a much longer time for
-        // reasons external to the device than 'not joining'
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
-                PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS,
-                Integer.toString(300000), false);
-        // Simulate re-installation of new NetworkStack with rollbacks enabled
-        installNetworkStackPackage();
-    }
-
-    @Test
-    public void testNetworkPassedDoesNotRollback_Phase2() throws Exception {
-        RollbackManager rm = RollbackUtils.getRollbackManager();
-        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
-                        getNetworkStackPackageName())).isNotNull();
-    }
-
-    @Test
-    public void testNetworkPassedDoesNotRollback_Phase3() throws Exception {
-        // Sleep for > health check deadline. We expect no rollback should happen during sleeping.
-        // If the device reboots for rollback, this device test will fail as well as the host test.
-        Thread.sleep(TimeUnit.SECONDS.toMillis(310));
-        RollbackManager rm = RollbackUtils.getRollbackManager();
-        assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
-                        getNetworkStackPackageName())).isNull();
-    }
-
     private static String getModuleMetadataPackageName() {
         return InstrumentationRegistry.getInstrumentation().getContext()
                 .getResources().getString(R.string.config_defaultModuleMetadataProvider);
@@ -554,12 +477,6 @@
         InstallUtils.waitForSessionReady(committed.getCommittedSessionId());
     }
 
-    private static void runShellCommand(String cmd) {
-        ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
-                .executeShellCommand(cmd);
-        IoUtils.closeQuietly(pfd);
-    }
-
     @Test
     public void isCheckpointSupported() {
         Context context = InstrumentationRegistry.getInstrumentation().getContext();
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 3c5eaef..c3fd962 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -228,39 +228,6 @@
     }
 
     /**
-     * Tests passed network health check does not trigger watchdog staged rollbacks.
-     */
-    @Test
-    public void testNetworkPassedDoesNotRollback() throws Exception {
-        runPhase("testNetworkPassedDoesNotRollback_Phase1");
-        // Reboot device to activate staged package
-        getDevice().reboot();
-
-        // Verify rollback was enabled
-        runPhase("testNetworkPassedDoesNotRollback_Phase2");
-
-        // Connect to internet so network health check passes
-        getDevice().executeShellCommand("svc wifi enable");
-        getDevice().executeShellCommand("svc data enable");
-
-        // Wait for device available because emulator device may restart after turning
-        // on mobile data
-        getDevice().waitForDeviceAvailable();
-
-        // Verify rollback was not executed after health check deadline
-        runPhase("testNetworkPassedDoesNotRollback_Phase3");
-        InputStreamSource logcatStream = mReceiver.getLogcatData();
-        try {
-            List<String> watchdogEvents = getWatchdogLoggingEvents(logcatStream);
-            assertEquals(watchdogEventOccurred(watchdogEvents, null, null,
-                    REASON_EXPLICIT_HEALTH_CHECK, null), false);
-        } finally {
-            logcatStream.close();
-        }
-
-    }
-
-    /**
      * Tests rolling back user data where there are multiple rollbacks for that package.
      */
     @Test
@@ -489,11 +456,6 @@
         }
     }
 
-    private String getNetworkStackPath() throws Exception {
-        // Find the NetworkStack path (can be NetworkStack.apk or NetworkStackNext.apk)
-        return getDevice().executeShellCommand("ls /system/priv-app/NetworkStack*/*.apk");
-    }
-
     private boolean isCheckpointSupported() throws Exception {
         try {
             runPhase("isCheckpointSupported");
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 0628691..8eb5cfa 100644
--- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -146,17 +146,14 @@
 
     @Test
     public void testConnectivityReportEquals() {
-        assertEquals(createSampleConnectivityReport(), createSampleConnectivityReport());
-        assertEquals(createDefaultConnectivityReport(), createDefaultConnectivityReport());
+        final ConnectivityReport defaultReport = createDefaultConnectivityReport();
+        final ConnectivityReport sampleReport = createSampleConnectivityReport();
+        assertEquals(sampleReport, createSampleConnectivityReport());
+        assertEquals(defaultReport, createDefaultConnectivityReport());
 
-        final LinkProperties linkProperties = new LinkProperties();
-        linkProperties.setInterfaceName(INTERFACE_NAME);
-
-        final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
-        networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
-
-        final PersistableBundle bundle = new PersistableBundle();
-        bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+        final LinkProperties linkProperties = sampleReport.getLinkProperties();
+        final NetworkCapabilities networkCapabilities = sampleReport.getNetworkCapabilities();
+        final PersistableBundle bundle = sampleReport.getAdditionalInfo();
 
         assertNotEquals(
                 createDefaultConnectivityReport(),
@@ -206,39 +203,104 @@
     }
 
     private DataStallReport createSampleDataStallReport() {
+        final LinkProperties linkProperties = new LinkProperties();
+        linkProperties.setInterfaceName(INTERFACE_NAME);
+
         final PersistableBundle bundle = new PersistableBundle();
         bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
-        return new DataStallReport(new Network(NET_ID), TIMESTAMP, DETECTION_METHOD, bundle);
+
+        final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+        networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+
+        return new DataStallReport(
+                new Network(NET_ID),
+                TIMESTAMP,
+                DETECTION_METHOD,
+                linkProperties,
+                networkCapabilities,
+                bundle);
     }
 
     private DataStallReport createDefaultDataStallReport() {
-        return new DataStallReport(new Network(0), 0L, 0, PersistableBundle.EMPTY);
+        return new DataStallReport(
+                new Network(0),
+                0L,
+                0,
+                new LinkProperties(),
+                new NetworkCapabilities(),
+                PersistableBundle.EMPTY);
     }
 
     @Test
     public void testDataStallReportEquals() {
-        assertEquals(createSampleDataStallReport(), createSampleDataStallReport());
-        assertEquals(createDefaultDataStallReport(), createDefaultDataStallReport());
+        final DataStallReport defaultReport = createDefaultDataStallReport();
+        final DataStallReport sampleReport = createSampleDataStallReport();
+        assertEquals(sampleReport, createSampleDataStallReport());
+        assertEquals(defaultReport, createDefaultDataStallReport());
 
-        final PersistableBundle bundle = new PersistableBundle();
-        bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+        final LinkProperties linkProperties = sampleReport.getLinkProperties();
+        final NetworkCapabilities networkCapabilities = sampleReport.getNetworkCapabilities();
+        final PersistableBundle bundle = sampleReport.getStallDetails();
 
         assertNotEquals(
-                createDefaultDataStallReport(),
-                new DataStallReport(new Network(NET_ID), 0L, 0, PersistableBundle.EMPTY));
+                defaultReport,
+                new DataStallReport(
+                        new Network(NET_ID),
+                        0L,
+                        0,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
         assertNotEquals(
-                createDefaultDataStallReport(),
-                new DataStallReport(new Network(0), TIMESTAMP, 0, PersistableBundle.EMPTY));
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        TIMESTAMP,
+                        0,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
         assertNotEquals(
-                createDefaultDataStallReport(),
-                new DataStallReport(new Network(0), 0L, DETECTION_METHOD, PersistableBundle.EMPTY));
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        0L,
+                        DETECTION_METHOD,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
         assertNotEquals(
-                createDefaultDataStallReport(), new DataStallReport(new Network(0), 0L, 0, bundle));
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        0L,
+                        0,
+                        linkProperties,
+                        new NetworkCapabilities(),
+                        PersistableBundle.EMPTY));
+        assertNotEquals(
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        0L,
+                        0,
+                        new LinkProperties(),
+                        networkCapabilities,
+                        PersistableBundle.EMPTY));
+        assertNotEquals(
+                defaultReport,
+                new DataStallReport(
+                        new Network(0),
+                        0L,
+                        0,
+                        new LinkProperties(),
+                        new NetworkCapabilities(),
+                        bundle));
     }
 
     @Test
     public void testDataStallReportParcelUnparcel() {
-        assertParcelSane(createSampleDataStallReport(), 4);
+        assertParcelSane(createSampleDataStallReport(), 6);
     }
 
     @Test
diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
new file mode 100644
index 0000000..86c9116
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity
+
+import android.net.NetworkRequest
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NetworkRankerTest {
+    private val ranker = NetworkRanker()
+
+    private fun makeNai(satisfy: Boolean, score: Int) = mock(NetworkAgentInfo::class.java).also {
+        doReturn(satisfy).`when`(it).satisfies(any())
+        doReturn(score).`when`(it).currentScore
+    }
+
+    @Test
+    fun testGetBestNetwork() {
+        val scores = listOf(20, 50, 90, 60, 23, 68)
+        val nais = scores.map { makeNai(true, it) }
+        val bestNetwork = nais[2] // The one with the top score
+        val someRequest = mock(NetworkRequest::class.java)
+        assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais))
+    }
+
+    @Test
+    fun testIgnoreNonSatisfying() {
+        val nais = listOf(makeNai(true, 20), makeNai(true, 50), makeNai(false, 90),
+                makeNai(false, 60), makeNai(true, 23), makeNai(false, 68))
+        val bestNetwork = nais[1] // Top score that's satisfying
+        val someRequest = mock(NetworkRequest::class.java)
+        assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais))
+    }
+
+    @Test
+    fun testNoMatch() {
+        val nais = listOf(makeNai(false, 20), makeNai(false, 50), makeNai(false, 90))
+        val someRequest = mock(NetworkRequest::class.java)
+        assertNull(ranker.getBestNetwork(someRequest, nais))
+    }
+
+    @Test
+    fun testEmpty() {
+        val someRequest = mock(NetworkRequest::class.java)
+        assertNull(ranker.getBestNetwork(someRequest, emptyList()))
+    }
+
+    // Make sure the ranker is "stable" (as in stable sort), that is, it always returns the FIRST
+    // network satisfying the request if multiple of them have the same score.
+    @Test
+    fun testStable() {
+        val nais1 = listOf(makeNai(true, 30), makeNai(true, 30), makeNai(true, 30),
+                makeNai(true, 30), makeNai(true, 30), makeNai(true, 30))
+        val someRequest = mock(NetworkRequest::class.java)
+        assertEquals(nais1[0], ranker.getBestNetwork(someRequest, nais1))
+
+        val nais2 = listOf(makeNai(true, 30), makeNai(true, 50), makeNai(true, 20),
+                makeNai(true, 50), makeNai(true, 50), makeNai(true, 40))
+        assertEquals(nais2[1], ranker.getBestNetwork(someRequest, nais2))
+    }
+}
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 31aa249..e7a8203 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -1188,7 +1188,7 @@
     }
 
     // Fully qualify the activity name
-    ssize_t idx = name.find(".");
+    ssize_t idx = name.find('.');
     if (idx == 0) {
       name = package + name;
     } else if (idx < 0) {
@@ -2138,7 +2138,7 @@
     size_t pos = file_path.find("lib/");
     if (pos != std::string::npos) {
       file_path = file_path.substr(pos + 4);
-      pos = file_path.find("/");
+      pos = file_path.find('/');
       if (pos != std::string::npos) {
         file_path = file_path.substr(0, pos);
       }
diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp
index 10e504e..09ea03b 100644
--- a/tools/aapt2/java/ManifestClassGenerator.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator.cpp
@@ -39,7 +39,7 @@
   }
 
   // Normalize only the java identifier, leave the original value unchanged.
-  if (result.find("-") != std::string::npos) {
+  if (result.find('-') != std::string::npos) {
     result = JavaClassGenerator::TransformToFieldName(result);
   }
 
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 0cce23d..dc5ac1e 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -2140,7 +2140,8 @@
     public boolean isEnterprise() {
         return (allowedKeyManagement.get(KeyMgmt.WPA_EAP)
                 || allowedKeyManagement.get(KeyMgmt.IEEE8021X)
-                || allowedKeyManagement.get(KeyMgmt.SUITE_B_192))
+                || allowedKeyManagement.get(KeyMgmt.SUITE_B_192)
+                || allowedKeyManagement.get(KeyMgmt.WAPI_CERT))
                 && enterpriseConfig != null
                 && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE;
     }
@@ -2416,6 +2417,9 @@
             if (allowedKeyManagement.get(KeyMgmt.SUITE_B_192)) {
                 keyMgmt += KeyMgmt.strings[KeyMgmt.SUITE_B_192];
             }
+            if (allowedKeyManagement.get(KeyMgmt.WAPI_CERT)) {
+                keyMgmt += KeyMgmt.strings[KeyMgmt.WAPI_CERT];
+            }
 
             if (TextUtils.isEmpty(keyMgmt)) {
                 throw new IllegalStateException("Not an EAP network");