Merge changes If2a41e5e,If987a317,I86d3d50f

* changes:
  Remove redundant modifiers from the constants
  Add support for 2.2::FingerprintAcquiredInfo::START
  Use @2.2 callback with @2.2 HAL
diff --git a/Android.bp b/Android.bp
index 2dc8a4c..df852bd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -458,7 +458,7 @@
     libs: [
         "framework-appsearch-stubs",
         "framework-sdkextensions-stubs-systemapi",
-        "framework-statsd", // TODO(b/146167933): Use framework-statsd-stubs
+        "framework-statsd-stubs-module_libs_api",
         "framework-permission-stubs-systemapi",
         "framework-wifi-stubs",
         "ike-stubs",
@@ -511,8 +511,7 @@
         "framework-mediaprovider-stubs-systemapi",
         "framework-permission-stubs-systemapi",
         "framework-sdkextensions-stubs-systemapi",
-        // TODO(b/146167933): Use framework-statsd-stubs instead.
-        "framework-statsd",
+        "framework-statsd-stubs-module_libs_api",
         "framework-wifi-stubs",
         "ike-stubs",
         "framework-tethering-stubs",
@@ -616,6 +615,18 @@
     out: ["com/android/internal/util/FrameworkStatsLog.java"],
 }
 
+java_library {
+    name: "uieventloggerlib",
+    srcs: [
+        "core/java/com/android/internal/logging/UiEvent.java",
+        "core/java/com/android/internal/logging/UiEventLogger.java",
+        "core/java/com/android/internal/logging/UiEventLoggerImpl.java",
+        "core/java/com/android/internal/logging/InstanceId.java",
+        "core/java/com/android/internal/logging/InstanceIdSequence.java",
+        ":statslog-framework-java-gen",
+    ],
+}
+
 gensrcs {
     name: "framework-javastream-protos",
     depfile: true,
diff --git a/StubLibraries.bp b/StubLibraries.bp
index d4db737..50d23ad2 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -190,7 +190,6 @@
 droidstubs {
     name: "module-lib-api",
     defaults: ["metalava-api-stubs-default"],
-    libs: ["framework-all"],
     arg_files: ["core/res/AndroidManifest.xml"],
     args: metalava_framework_docs_args + module_libs,
     check_api: {
@@ -222,7 +221,6 @@
 droidstubs {
     name: "module-lib-api-stubs-docs",
     defaults: ["metalava-api-stubs-default"],
-    libs: ["framework-all"],
     arg_files: ["core/res/AndroidManifest.xml"],
     args: metalava_framework_docs_args + priv_apps + module_libs,
 }
@@ -234,6 +232,9 @@
 
 java_defaults {
     name: "framework-stubs-default",
+    libs: [ "stub-annotations" ],
+    static_libs: [ "private-stub-annotations-jar" ],
+    sdk_version: "core_current",
     errorprone: {
         javacflags: [
             "-XepDisableAllChecks",
@@ -249,61 +250,25 @@
 
 java_library_static {
     name: "android_stubs_current",
-    srcs: [
-        ":api-stubs-docs",
-    ],
-    libs: [
-        "stub-annotations",
-    ],
-    static_libs: [
-        "private-stub-annotations-jar",
-    ],
+    srcs: [ ":api-stubs-docs" ],
     defaults: ["framework-stubs-default"],
-    sdk_version: "core_current",
 }
 
 java_library_static {
     name: "android_system_stubs_current",
-    srcs: [
-        ":system-api-stubs-docs",
-    ],
-    libs: [
-        "stub-annotations",
-    ],
-    static_libs: [
-        "private-stub-annotations-jar",
-    ],
+    srcs: [ ":system-api-stubs-docs" ],
     defaults: ["framework-stubs-default"],
-    sdk_version: "core_current",
 }
 
 java_library_static {
     name: "android_test_stubs_current",
-    srcs: [
-        ":test-api-stubs-docs",
-    ],
-    libs: [
-        "stub-annotations",
-    ],
-    static_libs: [
-        "private-stub-annotations-jar",
-    ],
+    srcs: [ ":test-api-stubs-docs" ],
     defaults: ["framework-stubs-default"],
-    sdk_version: "core_current",
 }
 
 java_library_static {
     name: "android_module_lib_stubs_current",
-    srcs: [
-        ":module-lib-api-stubs-docs",
-    ],
-    libs: [
-        "stub-annotations",
-        "framework-all",
-    ],
-    static_libs: [
-        "private-stub-annotations-jar",
-    ],
+    srcs: [ ":module-lib-api-stubs-docs" ],
     defaults: ["framework-stubs-default"],
 }
 
diff --git a/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java b/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
new file mode 100644
index 0000000..14282bf
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
@@ -0,0 +1,240 @@
+/*
+ * 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.view;
+
+import android.content.Context;
+import android.graphics.Matrix;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.PathParser;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class CutoutSpecificationBenchmark {
+    private static final String TAG = "CutoutSpecificationBenchmark";
+
+    private static final String BOTTOM_MARKER = "@bottom";
+    private static final String DP_MARKER = "@dp";
+    private static final String RIGHT_MARKER = "@right";
+    private static final String LEFT_MARKER = "@left";
+
+    private static final String DOUBLE_CUTOUT_SPEC = "M 0,0\n"
+            + "L -72, 0\n"
+            + "L -69.9940446283, 20.0595537175\n"
+            + "C -69.1582133885, 28.4178661152 -65.2, 32.0 -56.8, 32.0\n"
+            + "L 56.8, 32.0\n"
+            + "C 65.2, 32.0 69.1582133885, 28.4178661152 69.9940446283, 20.0595537175\n"
+            + "L 72, 0\n"
+            + "Z\n"
+            + "@bottom\n"
+            + "M 0,0\n"
+            + "L -72, 0\n"
+            + "L -69.9940446283, -20.0595537175\n"
+            + "C -69.1582133885, -28.4178661152 -65.2, -32.0 -56.8, -32.0\n"
+            + "L 56.8, -32.0\n"
+            + "C 65.2, -32.0 69.1582133885, -28.4178661152 69.9940446283, -20.0595537175\n"
+            + "L 72, 0\n"
+            + "Z\n"
+            + "@dp";
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    private Context mContext;
+    private DisplayMetrics mDisplayMetrics;
+
+    /**
+     * Setup the necessary member field used by test methods.
+     */
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+        mDisplayMetrics = new DisplayMetrics();
+        mContext.getDisplay().getRealMetrics(mDisplayMetrics);
+    }
+
+
+    private static void toRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
+        final RectF rectF = new RectF();
+        p.computeBounds(rectF, false /* unused */);
+        rectF.round(inoutRect);
+        inoutRegion.op(inoutRect, Region.Op.UNION);
+    }
+
+    private static void oldMethodParsingSpec(String spec, int displayWidth, int displayHeight,
+            float density) {
+        Path p = null;
+        Rect boundTop = null;
+        Rect boundBottom = null;
+        Rect safeInset = new Rect();
+        String bottomSpec = null;
+        if (!TextUtils.isEmpty(spec)) {
+            spec = spec.trim();
+            final float offsetX;
+            if (spec.endsWith(RIGHT_MARKER)) {
+                offsetX = displayWidth;
+                spec = spec.substring(0, spec.length() - RIGHT_MARKER.length()).trim();
+            } else if (spec.endsWith(LEFT_MARKER)) {
+                offsetX = 0;
+                spec = spec.substring(0, spec.length() - LEFT_MARKER.length()).trim();
+            } else {
+                offsetX = displayWidth / 2f;
+            }
+            final boolean inDp = spec.endsWith(DP_MARKER);
+            if (inDp) {
+                spec = spec.substring(0, spec.length() - DP_MARKER.length());
+            }
+
+            if (spec.contains(BOTTOM_MARKER)) {
+                String[] splits = spec.split(BOTTOM_MARKER, 2);
+                spec = splits[0].trim();
+                bottomSpec = splits[1].trim();
+            }
+
+            final Matrix m = new Matrix();
+            final Region r = Region.obtain();
+            if (!spec.isEmpty()) {
+                try {
+                    p = PathParser.createPathFromPathData(spec);
+                } catch (Throwable e) {
+                    Log.wtf(TAG, "Could not inflate cutout: ", e);
+                }
+
+                if (p != null) {
+                    if (inDp) {
+                        m.postScale(density, density);
+                    }
+                    m.postTranslate(offsetX, 0);
+                    p.transform(m);
+
+                    boundTop = new Rect();
+                    toRectAndAddToRegion(p, r, boundTop);
+                    safeInset.top = boundTop.bottom;
+                }
+            }
+
+            if (bottomSpec != null) {
+                int bottomInset = 0;
+                Path bottomPath = null;
+                try {
+                    bottomPath = PathParser.createPathFromPathData(bottomSpec);
+                } catch (Throwable e) {
+                    Log.wtf(TAG, "Could not inflate bottom cutout: ", e);
+                }
+
+                if (bottomPath != null) {
+                    // Keep top transform
+                    m.postTranslate(0, displayHeight);
+                    bottomPath.transform(m);
+                    p.addPath(bottomPath);
+                    boundBottom = new Rect();
+                    toRectAndAddToRegion(bottomPath, r, boundBottom);
+                    bottomInset = displayHeight - boundBottom.top;
+                }
+                safeInset.bottom = bottomInset;
+            }
+        }
+    }
+
+    @Test
+    public void parseByOldMethodForDoubleCutout() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            oldMethodParsingSpec(DOUBLE_CUTOUT_SPEC, mDisplayMetrics.widthPixels,
+                    mDisplayMetrics.heightPixels, mDisplayMetrics.density);
+        }
+    }
+
+    @Test
+    public void parseByNewMethodForDoubleCutout() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            new CutoutSpecification.Parser(mDisplayMetrics.density,
+                    mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)
+                    .parse(DOUBLE_CUTOUT_SPEC);
+        }
+    }
+
+    @Test
+    public void parseLongEdgeCutout() {
+        final String spec = "M 0,0\n"
+                + "H 48\n"
+                + "V 48\n"
+                + "H -48\n"
+                + "Z\n"
+                + "@left\n"
+                + "@center_vertical\n"
+                + "M 0,0\n"
+                + "H 48\n"
+                + "V 48\n"
+                + "H -48\n"
+                + "Z\n"
+                + "@left\n"
+                + "@center_vertical\n"
+                + "M 0,0\n"
+                + "H -48\n"
+                + "V 48\n"
+                + "H 48\n"
+                + "Z\n"
+                + "@right\n"
+                + "@dp";
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            new CutoutSpecification.Parser(mDisplayMetrics.density,
+                    mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec);
+        }
+    }
+
+    @Test
+    public void parseShortEdgeCutout() {
+        final String spec = "M 0,0\n"
+                + "H 48\n"
+                + "V 48\n"
+                + "H -48\n"
+                + "Z\n"
+                + "@bottom\n"
+                + "M 0,0\n"
+                + "H 48\n"
+                + "V -48\n"
+                + "H -48\n"
+                + "Z\n"
+                + "@dp";
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            new CutoutSpecification.Parser(mDisplayMetrics.density,
+                    mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec);
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
index b6e39e1..8633c96 100644
--- a/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/core/src/android/wm/RelayoutPerfTest.java
@@ -125,7 +125,9 @@
         final WindowManager.LayoutParams mParams;
         final int mWidth;
         final int mHeight;
+        final Point mOutSurfaceSize = new Point();
         final SurfaceControl mOutSurfaceControl;
+        final SurfaceControl mOutBlastSurfaceControl = new SurfaceControl();
 
         final IntSupplier mViewVisibility;
 
@@ -150,7 +152,8 @@
                         mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrame,
                         mOutContentInsets, mOutVisibleInsets, mOutStableInsets,
                         mOutBackDropFrame, mOutDisplayCutout, mOutMergedConfiguration,
-                        mOutSurfaceControl, mOutInsetsState, new Point(), new SurfaceControl());
+                        mOutSurfaceControl, mOutInsetsState, mOutSurfaceSize,
+                        mOutBlastSurfaceControl);
             }
         }
     }
diff --git a/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java
index 62e9ba8..9e17e94 100644
--- a/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java
+++ b/apct-tests/perftests/core/src/android/wm/WindowManagerPerfTestBase.java
@@ -20,15 +20,19 @@
 
 import android.app.Activity;
 import android.app.UiAutomation;
+import android.content.Context;
 import android.content.Intent;
+import android.os.BatteryManager;
 import android.os.ParcelFileDescriptor;
 import android.perftests.utils.PerfTestActivity;
+import android.provider.Settings;
 
 import androidx.test.rule.ActivityTestRule;
 import androidx.test.runner.lifecycle.ActivityLifecycleCallback;
 import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
 import androidx.test.runner.lifecycle.Stage;
 
+import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
@@ -52,18 +56,36 @@
      */
     static final File BASE_OUT_PATH = new File("/data/local/CorePerfTests");
 
+    private static int sOriginalStayOnWhilePluggedIn;
+
     @BeforeClass
     public static void setUpOnce() {
+        final Context context = getInstrumentation().getContext();
+        sOriginalStayOnWhilePluggedIn = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+        // Keep the device awake during testing.
+        setStayOnWhilePluggedIn(BatteryManager.BATTERY_PLUGGED_USB);
+
         if (!BASE_OUT_PATH.exists()) {
             executeShellCommand("mkdir -p " + BASE_OUT_PATH);
         }
         // In order to be closer to the real use case.
         executeShellCommand("input keyevent KEYCODE_WAKEUP");
         executeShellCommand("wm dismiss-keyguard");
-        getInstrumentation().getContext().startActivity(new Intent(Intent.ACTION_MAIN)
+        context.startActivity(new Intent(Intent.ACTION_MAIN)
                 .addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
     }
 
+    @AfterClass
+    public static void tearDownOnce() {
+        setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn);
+    }
+
+    private static void setStayOnWhilePluggedIn(int value) {
+        executeShellCommand(String.format("settings put global %s %d",
+                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value));
+    }
+
     /**
      * Executes shell command with reading the output. It may also used to block until the current
      * command is completed.
@@ -97,7 +119,7 @@
      */
     static class PerfTestActivityRule extends ActivityTestRule<PerfTestActivity> {
         private final Intent mStartIntent =
-                new Intent().putExtra(PerfTestActivity.INTENT_EXTRA_KEEP_SCREEN_ON, true);
+                new Intent(getInstrumentation().getTargetContext(), PerfTestActivity.class);
         private final LifecycleListener mLifecycleListener = new LifecycleListener();
 
         PerfTestActivityRule() {
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index c458d11..661f32f 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.getFeatureId(),
+                context.getPackageName(),
                 context.getPackageManager().getLaunchIntentForPackage(packageName), null, null,
                 null, 0, 0, null, null, userId);
         attestTrue("User " + userId + " failed to start " + packageName,
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index e3c70d7..80def47 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -12,11 +12,27 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+genrule {
+    name: "statslog-statsd-java-gen",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --java $(out) --module statsd" +
+         " --javaPackage com.android.internal.util --javaClass StatsdStatsLog",
+    out: ["com/android/internal/util/StatsdStatsLog.java"],
+}
+
+java_library_static {
+    name: "statslog-statsd",
+    srcs: [
+        ":statslog-statsd-java-gen",
+    ],
+}
+
 filegroup {
     name: "framework-statsd-sources",
     srcs: [
         "java/**/*.java",
         ":statsd_java_aidl",
+        ":statslog-statsd-java-gen",
     ],
 }
 
@@ -42,8 +58,7 @@
     hostdex: true, // for hiddenapi check
     visibility: [
         "//frameworks/base/apex/statsd:__subpackages__",
-        //TODO(b/146167933) remove this when framework is built with framework-statsd-stubs
-        "//frameworks/base",
+        //TODO(b/146167933) remove this
         "//frameworks/opt/net/wifi/service",
     ],
     apex_available: [
@@ -103,7 +118,6 @@
     sdk_version: "core_platform",
 }
 
-// TODO(b/146167933): Use these stubs in frameworks/base/Android.bp
 java_library {
     name: "framework-statsd-stubs-systemapi",
     srcs: [ ":framework-statsd-stubs-srcs-systemapi" ],
diff --git a/apex/statsd/framework/java/android/app/StatsManager.java b/apex/statsd/framework/java/android/app/StatsManager.java
index 411482b..526d17f 100644
--- a/apex/statsd/framework/java/android/app/StatsManager.java
+++ b/apex/statsd/framework/java/android/app/StatsManager.java
@@ -32,7 +32,7 @@
 import android.os.RemoteException;
 import android.os.StatsFrameworkInitializer;
 import android.util.AndroidException;
-import android.util.Slog;
+import android.util.Log;
 import android.util.StatsEvent;
 import android.util.StatsEventParcel;
 
@@ -155,7 +155,7 @@
                 // can throw IllegalArgumentException
                 service.addConfiguration(configKey, config, mContext.getOpPackageName());
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsmanager when adding configuration");
+                Log.e(TAG, "Failed to connect to statsmanager when adding configuration");
                 throw new StatsUnavailableException("could not connect", e);
             } catch (SecurityException e) {
                 throw new StatsUnavailableException(e.getMessage(), e);
@@ -191,7 +191,7 @@
                 IStatsManagerService service = getIStatsManagerServiceLocked();
                 service.removeConfiguration(configKey, mContext.getOpPackageName());
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsmanager when removing configuration");
+                Log.e(TAG, "Failed to connect to statsmanager when removing configuration");
                 throw new StatsUnavailableException("could not connect", e);
             } catch (SecurityException e) {
                 throw new StatsUnavailableException(e.getMessage(), e);
@@ -258,7 +258,7 @@
                             mContext.getOpPackageName());
                 }
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsmanager when adding broadcast subscriber",
+                Log.e(TAG, "Failed to connect to statsmanager when adding broadcast subscriber",
                         e);
                 throw new StatsUnavailableException("could not connect", e);
             } catch (SecurityException e) {
@@ -311,7 +311,7 @@
                 }
 
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsmanager when registering data listener.");
+                Log.e(TAG, "Failed to connect to statsmanager when registering data listener.");
                 throw new StatsUnavailableException("could not connect", e);
             } catch (SecurityException e) {
                 throw new StatsUnavailableException(e.getMessage(), e);
@@ -348,7 +348,7 @@
                 }
 
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsmanager "
+                Log.e(TAG, "Failed to connect to statsmanager "
                         + "when registering active configs listener.");
                 throw new StatsUnavailableException("could not connect", e);
             } catch (SecurityException e) {
@@ -387,7 +387,7 @@
                 IStatsManagerService service = getIStatsManagerServiceLocked();
                 return service.getData(configKey, mContext.getOpPackageName());
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsmanager when getting data");
+                Log.e(TAG, "Failed to connect to statsmanager when getting data");
                 throw new StatsUnavailableException("could not connect", e);
             } catch (SecurityException e) {
                 throw new StatsUnavailableException(e.getMessage(), e);
@@ -424,7 +424,7 @@
                 IStatsManagerService service = getIStatsManagerServiceLocked();
                 return service.getMetadata(mContext.getOpPackageName());
             } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsmanager when getting metadata");
+                Log.e(TAG, "Failed to connect to statsmanager when getting metadata");
                 throw new StatsUnavailableException("could not connect", e);
             } catch (SecurityException e) {
                 throw new StatsUnavailableException(e.getMessage(), e);
@@ -464,7 +464,7 @@
                 return service.getRegisteredExperimentIds();
             } catch (RemoteException e) {
                 if (DEBUG) {
-                    Slog.d(TAG,
+                    Log.d(TAG,
                             "Failed to connect to StatsManagerService when getting "
                                     + "registered experiment IDs");
                 }
@@ -555,7 +555,7 @@
                     try {
                         resultReceiver.pullFinished(atomTag, success, parcels);
                     } catch (RemoteException e) {
-                        Slog.w(TAG, "StatsPullResultReceiver failed for tag " + mAtomId);
+                        Log.w(TAG, "StatsPullResultReceiver failed for tag " + mAtomId);
                     }
                 });
             } finally {
diff --git a/apex/statsd/framework/java/android/os/StatsDimensionsValue.java b/apex/statsd/framework/java/android/os/StatsDimensionsValue.java
index 71d4359..35273da 100644
--- a/apex/statsd/framework/java/android/os/StatsDimensionsValue.java
+++ b/apex/statsd/framework/java/android/os/StatsDimensionsValue.java
@@ -16,7 +16,7 @@
 package android.os;
 
 import android.annotation.SystemApi;
-import android.util.Slog;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -129,7 +129,7 @@
                 mValue = values;
                 break;
             default:
-                Slog.w(TAG, "StatsDimensionsValueParcel contains bad valueType: " + mValueType);
+                Log.w(TAG, "StatsDimensionsValueParcel contains bad valueType: " + mValueType);
                 mValue = null;
                 break;
         }
@@ -155,7 +155,7 @@
         try {
             if (mValueType == STRING_VALUE_TYPE) return (String) mValue;
         } catch (ClassCastException e) {
-            Slog.w(TAG, "Failed to successfully get value", e);
+            Log.w(TAG, "Failed to successfully get value", e);
         }
         return null;
     }
@@ -169,7 +169,7 @@
         try {
             if (mValueType == INT_VALUE_TYPE) return (Integer) mValue;
         } catch (ClassCastException e) {
-            Slog.w(TAG, "Failed to successfully get value", e);
+            Log.w(TAG, "Failed to successfully get value", e);
         }
         return 0;
     }
@@ -183,7 +183,7 @@
         try {
             if (mValueType == LONG_VALUE_TYPE) return (Long) mValue;
         } catch (ClassCastException e) {
-            Slog.w(TAG, "Failed to successfully get value", e);
+            Log.w(TAG, "Failed to successfully get value", e);
         }
         return 0;
     }
@@ -198,7 +198,7 @@
         try {
             if (mValueType == BOOLEAN_VALUE_TYPE) return (Boolean) mValue;
         } catch (ClassCastException e) {
-            Slog.w(TAG, "Failed to successfully get value", e);
+            Log.w(TAG, "Failed to successfully get value", e);
         }
         return false;
     }
@@ -212,7 +212,7 @@
         try {
             if (mValueType == FLOAT_VALUE_TYPE) return (Float) mValue;
         } catch (ClassCastException e) {
-            Slog.w(TAG, "Failed to successfully get value", e);
+            Log.w(TAG, "Failed to successfully get value", e);
         }
         return 0;
     }
@@ -238,7 +238,7 @@
             }
             return copy;
         } catch (ClassCastException e) {
-            Slog.w(TAG, "Failed to successfully get value", e);
+            Log.w(TAG, "Failed to successfully get value", e);
             return null;
         }
     }
@@ -297,7 +297,7 @@
             }
             return sb.toString();
         } catch (ClassCastException e) {
-            Slog.w(TAG, "Failed to successfully get value", e);
+            Log.w(TAG, "Failed to successfully get value", e);
         }
         return "";
     }
@@ -357,11 +357,11 @@
                     return true;
                 }
                 default:
-                    Slog.w(TAG, "readValue of an impossible type " + valueType);
+                    Log.w(TAG, "readValue of an impossible type " + valueType);
                     return false;
             }
         } catch (ClassCastException e) {
-            Slog.w(TAG, "writeValue cast failed", e);
+            Log.w(TAG, "writeValue cast failed", e);
             return false;
         }
     }
@@ -388,7 +388,7 @@
                 return values;
             }
             default:
-                Slog.w(TAG, "readValue of an impossible type " + valueType);
+                Log.w(TAG, "readValue of an impossible type " + valueType);
                 return null;
         }
     }
diff --git a/apex/statsd/framework/java/android/util/StatsLog.java b/apex/statsd/framework/java/android/util/StatsLog.java
index e7659d8..511bc01 100644
--- a/apex/statsd/framework/java/android/util/StatsLog.java
+++ b/apex/statsd/framework/java/android/util/StatsLog.java
@@ -26,10 +26,10 @@
 import android.content.Context;
 import android.os.IStatsd;
 import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.os.StatsFrameworkInitializer;
 import android.util.proto.ProtoOutputStream;
 
-import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.StatsdStatsLog;
 
 /**
  * StatsLog provides an API for developers to send events to statsd. The events can be used to
@@ -59,17 +59,17 @@
                 IStatsd service = getIStatsdLocked();
                 if (service == null) {
                     if (DEBUG) {
-                        Slog.d(TAG, "Failed to find statsd when logging start");
+                        Log.d(TAG, "Failed to find statsd when logging start");
                     }
                     return false;
                 }
                 service.sendAppBreadcrumbAtom(label,
-                        FrameworkStatsLog.APP_BREADCRUMB_REPORTED__STATE__START);
+                        StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__START);
                 return true;
             } catch (RemoteException e) {
                 sService = null;
                 if (DEBUG) {
-                    Slog.d(TAG, "Failed to connect to statsd when logging start");
+                    Log.d(TAG, "Failed to connect to statsd when logging start");
                 }
                 return false;
             }
@@ -88,17 +88,17 @@
                 IStatsd service = getIStatsdLocked();
                 if (service == null) {
                     if (DEBUG) {
-                        Slog.d(TAG, "Failed to find statsd when logging stop");
+                        Log.d(TAG, "Failed to find statsd when logging stop");
                     }
                     return false;
                 }
                 service.sendAppBreadcrumbAtom(
-                        label, FrameworkStatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP);
+                        label, StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__STOP);
                 return true;
             } catch (RemoteException e) {
                 sService = null;
                 if (DEBUG) {
-                    Slog.d(TAG, "Failed to connect to statsd when logging stop");
+                    Log.d(TAG, "Failed to connect to statsd when logging stop");
                 }
                 return false;
             }
@@ -117,17 +117,17 @@
                 IStatsd service = getIStatsdLocked();
                 if (service == null) {
                     if (DEBUG) {
-                        Slog.d(TAG, "Failed to find statsd when logging event");
+                        Log.d(TAG, "Failed to find statsd when logging event");
                     }
                     return false;
                 }
                 service.sendAppBreadcrumbAtom(
-                        label, FrameworkStatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED);
+                        label, StatsdStatsLog.APP_BREADCRUMB_REPORTED__STATE__UNSPECIFIED);
                 return true;
             } catch (RemoteException e) {
                 sService = null;
                 if (DEBUG) {
-                    Slog.d(TAG, "Failed to connect to statsd when logging event");
+                    Log.d(TAG, "Failed to connect to statsd when logging event");
                 }
                 return false;
             }
@@ -162,7 +162,7 @@
                     | EXPERIMENT_IDS_FIELD_ID,
                     id);
         }
-        FrameworkStatsLog.write(FrameworkStatsLog.BINARY_PUSH_STATE_CHANGED,
+        StatsdStatsLog.write(StatsdStatsLog.BINARY_PUSH_STATE_CHANGED,
                 trainName,
                 trainVersionCode,
                 (options & IStatsd.FLAG_REQUIRE_STAGING) > 0,
@@ -180,7 +180,10 @@
         if (sService != null) {
             return sService;
         }
-        sService = IStatsd.Stub.asInterface(ServiceManager.getService("stats"));
+        sService = IStatsd.Stub.asInterface(StatsFrameworkInitializer
+            .getStatsServiceManager()
+            .getStatsdServiceRegisterer()
+            .get());
         return sService;
     }
 
diff --git a/apex/statsd/service/Android.bp b/apex/statsd/service/Android.bp
index 9103848..0f8a151 100644
--- a/apex/statsd/service/Android.bp
+++ b/apex/statsd/service/Android.bp
@@ -1,17 +1,30 @@
 // Statsd Service jar, which will eventually be put in the statsd mainline apex.
 // service-statsd needs to be added to PRODUCT_UPDATABLE_SYSTEM_SERVER_JARS.
 // This jar will contain StatsCompanionService
+
+filegroup {
+    name: "service-statsd-sources",
+    srcs: [
+        "java/**/*.java",
+    ],
+}
+
 java_library {
     name: "service-statsd",
     installable: true,
 
     srcs: [
-        "java/**/*.java",
+        ":service-statsd-sources",
     ],
-    // TODO: link against the proper stubs (b/146084685).
+    // TODO(b/146209659): Use system_current instead once framework-statsd compiles against
+    // system_current.
+    sdk_version: "core_platform",
     libs: [
-        "framework-minus-apex",
-        "services.core",
+        "framework-annotations-lib",
+        "framework-statsd",
+        // TODO(b/146758669): Remove this line after nullability annotations are system APIs.
+        "android_system_stubs_current",
+        "services-stubs",
     ],
     apex_available: [
         "com.android.os.statsd",
diff --git a/api/current.txt b/api/current.txt
index 826d409..fa232ac 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12575,6 +12575,7 @@
     method public int getLayoutDirection();
     method @NonNull public android.os.LocaleList getLocales();
     method public boolean isLayoutSizeAtLeast(int);
+    method public boolean isNightModeActive();
     method public boolean isScreenHdr();
     method public boolean isScreenRound();
     method public boolean isScreenWideColorGamut();
@@ -17032,6 +17033,7 @@
     field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
     field public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11; // 0xb
     field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
+    field public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; // 0xf
     field public static final int BIOMETRIC_SUCCESS = 0; // 0x0
   }
 
@@ -17066,6 +17068,7 @@
     field public static final int BIOMETRIC_ERROR_NO_BIOMETRICS = 11; // 0xb
     field public static final int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14; // 0xe
     field public static final int BIOMETRIC_ERROR_NO_SPACE = 4; // 0x4
+    field public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; // 0xf
     field public static final int BIOMETRIC_ERROR_TIMEOUT = 3; // 0x3
     field public static final int BIOMETRIC_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
     field public static final int BIOMETRIC_ERROR_USER_CANCELED = 10; // 0xa
@@ -31280,6 +31283,7 @@
     method public int getWifiState();
     method public boolean is5GHzBandSupported();
     method public boolean is6GHzBandSupported();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isAutoWakeupEnabled();
     method @Deprecated public boolean isDeviceToApRttSupported();
     method public boolean isEasyConnectSupported();
     method public boolean isEnhancedOpenSupported();
@@ -42599,6 +42603,7 @@
     method @NonNull public String getKeystoreAlias();
     method public int getPurposes();
     method @NonNull public String[] getSignaturePaddings();
+    method public int getUserAuthenticationType();
     method public int getUserAuthenticationValidityDurationSeconds();
     method @NonNull public boolean isDigestsSpecified();
     method public boolean isInvalidatedByBiometricEnrollment();
@@ -42633,9 +42638,10 @@
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
+    method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationParameters(@IntRange(from=0xffffffff) int, int);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean);
-    method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(@IntRange(from=0xffffffff) int);
+    method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(@IntRange(from=0xffffffff) int);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserConfirmationRequired(boolean);
     method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserPresenceRequired(boolean);
   }
@@ -42652,6 +42658,7 @@
     method public int getOrigin();
     method public int getPurposes();
     method @NonNull public String[] getSignaturePaddings();
+    method public int getUserAuthenticationType();
     method public int getUserAuthenticationValidityDurationSeconds();
     method public boolean isInsideSecureHardware();
     method public boolean isInvalidatedByBiometricEnrollment();
@@ -42675,6 +42682,8 @@
   }
 
   public abstract class KeyProperties {
+    field public static final int AUTH_BIOMETRIC_STRONG = 2; // 0x2
+    field public static final int AUTH_DEVICE_CREDENTIAL = 1; // 0x1
     field public static final String BLOCK_MODE_CBC = "CBC";
     field public static final String BLOCK_MODE_CTR = "CTR";
     field public static final String BLOCK_MODE_ECB = "ECB";
@@ -42721,6 +42730,7 @@
     method @Nullable public java.util.Date getKeyValidityStart();
     method public int getPurposes();
     method @NonNull public String[] getSignaturePaddings();
+    method public int getUserAuthenticationType();
     method public int getUserAuthenticationValidityDurationSeconds();
     method public boolean isDigestsSpecified();
     method public boolean isInvalidatedByBiometricEnrollment();
@@ -42746,9 +42756,10 @@
     method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
     method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
     method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
+    method @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationParameters(@IntRange(from=0xffffffff) int, int);
     method @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean);
     method @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean);
-    method @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(@IntRange(from=0xffffffff) int);
+    method @Deprecated @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(@IntRange(from=0xffffffff) int);
     method @NonNull public android.security.keystore.KeyProtection.Builder setUserConfirmationRequired(boolean);
     method @NonNull public android.security.keystore.KeyProtection.Builder setUserPresenceRequired(boolean);
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index 24936d5..6980069 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1471,10 +1471,10 @@
 
   public final class BluetoothA2dpSink implements android.bluetooth.BluetoothProfile {
     method public void finalize();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isAudioPlaying(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
-    field @RequiresPermission(android.Manifest.permission.BLUETOOTH) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isAudioPlaying(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
+    field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
   }
 
   public final class BluetoothAdapter {
@@ -1647,9 +1647,9 @@
   }
 
   public class BluetoothPbap implements android.bluetooth.BluetoothProfile {
-    method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
-    field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+    field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
   }
 
   public interface BluetoothProfile {
@@ -2007,18 +2007,15 @@
   }
 
   public final class InstallationFile implements android.os.Parcelable {
-    ctor public InstallationFile(@NonNull String, long, @Nullable byte[]);
+    ctor public InstallationFile(int, @NonNull String, long, @Nullable byte[], @Nullable byte[]);
     method public int describeContents();
-    method public int getFileType();
+    method public long getLengthBytes();
+    method public int getLocation();
     method @Nullable public byte[] getMetadata();
     method @NonNull public String getName();
-    method public long getSize();
+    method @Nullable public byte[] getSignature();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.InstallationFile> CREATOR;
-    field public static final int FILE_TYPE_APK = 0; // 0x0
-    field public static final int FILE_TYPE_LIB = 1; // 0x1
-    field public static final int FILE_TYPE_OBB = 2; // 0x2
-    field public static final int FILE_TYPE_UNKNOWN = -1; // 0xffffffff
   }
 
   public final class InstantAppInfo implements android.os.Parcelable {
@@ -4854,6 +4851,8 @@
 
   public class Tuner implements java.lang.AutoCloseable {
     ctor @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public Tuner(@NonNull android.content.Context, @NonNull String, int, @Nullable android.media.tv.tuner.Tuner.OnResourceLostListener);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int cancelScanning();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int cancelTuning();
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void clearOnTuneEventListener();
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void close();
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int connectCiCam(int);
@@ -4874,8 +4873,6 @@
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int setLna(boolean);
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void setOnTuneEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.frontend.OnTuneEventListener);
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int stopScan();
-    method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int stopTune();
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings);
     method @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public void updateResourcePriority(int, int);
   }
@@ -7717,6 +7714,7 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveBackupData();
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData();
     method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
+    method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setAutoWakeupEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
     method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMeteredOverridePasspoint(@NonNull String, int);
@@ -8242,8 +8240,8 @@
   public final class NativeScanResult implements android.os.Parcelable {
     ctor public NativeScanResult();
     method public int describeContents();
-    method @NonNull public byte[] getBssid();
-    method @NonNull public int getCapabilities();
+    method @Nullable public android.net.MacAddress getBssid();
+    method public int getCapabilities();
     method public int getFrequencyMhz();
     method @NonNull public byte[] getInformationElements();
     method @NonNull public java.util.List<android.net.wifi.wificond.RadioChainInfo> getRadioChainInfos();
@@ -8252,15 +8250,31 @@
     method public long getTsf();
     method public boolean isAssociated();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int BSS_CAPABILITY_APSD = 2048; // 0x800
+    field public static final int BSS_CAPABILITY_CF_POLLABLE = 4; // 0x4
+    field public static final int BSS_CAPABILITY_CF_POLL_REQUEST = 8; // 0x8
+    field public static final int BSS_CAPABILITY_CHANNEL_AGILITY = 128; // 0x80
+    field public static final int BSS_CAPABILITY_DELAYED_BLOCK_ACK = 16384; // 0x4000
+    field public static final int BSS_CAPABILITY_DSSS_OFDM = 8192; // 0x2000
+    field public static final int BSS_CAPABILITY_ESS = 1; // 0x1
+    field public static final int BSS_CAPABILITY_IBSS = 2; // 0x2
+    field public static final int BSS_CAPABILITY_IMMEDIATE_BLOCK_ACK = 32768; // 0x8000
+    field public static final int BSS_CAPABILITY_PBCC = 64; // 0x40
+    field public static final int BSS_CAPABILITY_PRIVACY = 16; // 0x10
+    field public static final int BSS_CAPABILITY_QOS = 512; // 0x200
+    field public static final int BSS_CAPABILITY_RADIO_MANAGEMENT = 4096; // 0x1000
+    field public static final int BSS_CAPABILITY_SHORT_PREAMBLE = 32; // 0x20
+    field public static final int BSS_CAPABILITY_SHORT_SLOT_TIME = 1024; // 0x400
+    field public static final int BSS_CAPABILITY_SPECTRUM_MANAGEMENT = 256; // 0x100
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.NativeScanResult> CREATOR;
   }
 
   public final class NativeWifiClient implements android.os.Parcelable {
-    ctor public NativeWifiClient(@NonNull byte[]);
+    ctor public NativeWifiClient(@Nullable android.net.MacAddress);
     method public int describeContents();
+    method @Nullable public android.net.MacAddress getMacAddress();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.wificond.NativeWifiClient> CREATOR;
-    field @NonNull public final byte[] macAddress;
   }
 
   public final class PnoNetwork implements android.os.Parcelable {
@@ -8305,7 +8319,7 @@
   public class WifiCondManager {
     method public void abortScan(@NonNull String);
     method public void enableVerboseLogging(boolean);
-    method @NonNull public java.util.List<java.lang.Integer> getChannelsMhzForBand(int);
+    method @NonNull public int[] getChannelsMhzForBand(int);
     method @Nullable public android.net.wifi.wificond.DeviceWiphyCapabilities getDeviceWiphyCapabilities(@NonNull String);
     method @NonNull public java.util.List<android.net.wifi.wificond.NativeScanResult> getScanResults(@NonNull String, int);
     method @Nullable public android.net.wifi.wificond.WifiCondManager.TxPacketCounters getTxPacketCounters(@NonNull String);
@@ -9680,7 +9694,6 @@
     field public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries";
     field public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent";
     field public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
-    field public static final String NETWORK_RECOMMENDATIONS_ENABLED = "network_recommendations_enabled";
     field public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
     field public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt";
     field public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled";
@@ -9695,7 +9708,7 @@
     field public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled";
     field public static final String WIFI_SCORE_PARAMS = "wifi_score_params";
     field public static final String WIFI_VERBOSE_LOGGING_ENABLED = "wifi_verbose_logging_enabled";
-    field public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
+    field @Deprecated public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
   }
 
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
@@ -10317,7 +10330,7 @@
 
   public abstract class DataLoaderService extends android.app.Service {
     ctor public DataLoaderService();
-    method @Nullable public android.service.dataloader.DataLoaderService.DataLoader onCreateDataLoader();
+    method @Nullable public android.service.dataloader.DataLoaderService.DataLoader onCreateDataLoader(@NonNull android.content.pm.DataLoaderParams);
   }
 
   public static interface DataLoaderService.DataLoader {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5e2dbf3..89b1798 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -127,7 +127,7 @@
         WallClockTimeShifted wall_clock_time_shifted = 45 [(module) = "framework"];
         AnomalyDetected anomaly_detected = 46;
         AppBreadcrumbReported app_breadcrumb_reported =
-                47 [(allow_from_any_uid) = true, (module) = "framework"];
+                47 [(allow_from_any_uid) = true, (module) = "statsd"];
         AppStartOccurred app_start_occurred = 48 [(module) = "framework"];
         AppStartCanceled app_start_canceled = 49 [(module) = "framework"];
         AppStartFullyDrawn app_start_fully_drawn = 50 [(module) = "framework"];
@@ -188,7 +188,7 @@
         ServiceStateChanged service_state_changed = 99 [(module) = "framework"];
         ServiceLaunchReported service_launch_reported = 100 [(module) = "framework"];
         FlagFlipUpdateOccurred flag_flip_update_occurred = 101 [(module) = "framework"];
-        BinaryPushStateChanged binary_push_state_changed = 102 [(module) = "framework"];
+        BinaryPushStateChanged binary_push_state_changed = 102 [(module) = "statsd"];
         DevicePolicyEvent device_policy_event = 103 [(module) = "framework"];
         DocsUIFileOperationCanceledReported docs_ui_file_op_canceled = 104 [(module) = "docsui"];
         DocsUIFileOperationCopyMoveModeReported docs_ui_file_op_copy_move_mode_reported =
@@ -391,6 +391,7 @@
         WifiHealthStatReported wifi_health_stat_reported = 251 [(module) = "wifi"];
         WifiFailureStatReported wifi_failure_stat_reported = 252 [(module) = "wifi"];
         WifiConnectionResultReported wifi_connection_result_reported = 253 [(module) = "wifi"];
+        AppFreezeChanged app_freeze_changed = 254 [(module) = "framework"];
         SdkExtensionStatus sdk_extension_status = 354;
     }
 
@@ -7518,6 +7519,9 @@
 
     // Button clicked by user - same as bit flags in buttons_presented with only single bit set
     optional int32 button_clicked = 5;
+
+    // id which identifies single session of user interacting with permission controller
+    optional int64 session_id = 6;
 }
 
 /**
@@ -8366,3 +8370,28 @@
     // "Failed" here can mean a symbol that wasn't meant to be visible was, or the other way around.
     optional int32 failed_call_symbol = 3;
 }
+
+/**
+ * Logs when an app is frozen or unfrozen.
+ *
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.java
+ */
+message AppFreezeChanged {
+  // The type of event.
+  enum Action {
+    UNKNOWN = 0;
+    FREEZE_APP = 1;
+    UNFREEZE_APP = 2;
+  }
+  optional Action action = 1;
+
+  // Pid of the process being frozen.
+  optional int32 pid = 2;
+
+  // Name of the process being frozen.
+  optional string process_name = 3;
+
+  // Time since last unfrozen.
+  optional int64 time_unfrozen_millis = 4;
+}
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index d79740b..9912d2b 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -76,6 +76,16 @@
     private final int mDescriptionResId;
 
     /**
+     * Resource id of the animated image of the accessibility shortcut target.
+     */
+    private final int mAnimatedImageRes;
+
+    /**
+     * Resource id of the html description of the accessibility shortcut target.
+     */
+    private final int mHtmlDescriptionRes;
+
+    /**
      * Creates a new instance.
      *
      * @param context Context for accessing resources.
@@ -119,6 +129,14 @@
             // Gets summary
             mSummaryResId = asAttributes.getResourceId(
                     com.android.internal.R.styleable.AccessibilityShortcutTarget_summary, 0);
+            // Gets animated image
+            mAnimatedImageRes = asAttributes.getResourceId(
+                    com.android.internal.R.styleable
+                            .AccessibilityShortcutTarget_animatedImageDrawable, 0);
+            // Gets html description
+            mHtmlDescriptionRes = asAttributes.getResourceId(
+                    com.android.internal.R.styleable.AccessibilityShortcutTarget_htmlDescription,
+                    0);
             asAttributes.recycle();
 
             if (mDescriptionResId == 0 || mSummaryResId == 0) {
@@ -172,6 +190,25 @@
     }
 
     /**
+     * The animated image resource id of the accessibility shortcut target.
+     *
+     * @return The animated image resource id.
+     */
+    public int getAnimatedImageRes() {
+        return mAnimatedImageRes;
+    }
+
+    /**
+     * The localized html description of the accessibility shortcut target.
+     *
+     * @return The localized html description.
+     */
+    @Nullable
+    public String loadHtmlDescription(@NonNull PackageManager packageManager) {
+        return loadResourceString(packageManager, mActivityInfo, mHtmlDescriptionRes);
+    }
+
+    /**
      * Gets string resource by the given activity and resource id.
      */
     @Nullable
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2319dd2..642f51b 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(),
-                            getFeatureId(), intent,
-                            intent.resolveTypeIfNeeded(getContentResolver()), mToken, mEmbeddedID,
-                            requestCode, ActivityManager.START_FLAG_ONLY_IF_NEEDED, null, options);
+                            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().getIntentSenderWithFeature(
-                        ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName, getFeatureId(),
+                ActivityManager.getService().getIntentSender(
+                        ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,
                         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 2838ad8..7ee4405 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().broadcastIntentWithFeature(
-                    null, null, intent, null, null, Activity.RESULT_OK, null, null,
+            getService().broadcastIntent(
+                    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 ec11043..c60f7bd 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -269,16 +269,13 @@
 
     public abstract void tempWhitelistForPendingIntent(int callerPid, int callerUid, int targetUid,
             long duration, String tag);
-
-    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 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 ComponentName startServiceInPackage(int uid, Intent service,
-            String resolvedType, boolean fgRequired, String callingPackage,
-            @Nullable String callingFeatureId, @UserIdInt int userId,
+            String resolvedType, boolean fgRequired, String callingPackage, @UserIdInt int userId,
             boolean allowBackgroundActivityStarts) throws TransactionTooLargeException;
 
     public abstract void disconnectActivityFromServices(Object connectionHolder);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6b5bfda..57cd894 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(), getFeatureId(), intent,
+                mMainThread.getApplicationThread(), getBasePackageName(), 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().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntent(
+                    mMainThread.getApplicationThread(), 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().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntent(
+                    mMainThread.getApplicationThread(), 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().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntent(
+                    mMainThread.getApplicationThread(), 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().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntent(
+                    mMainThread.getApplicationThread(), 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().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntent(
+                    mMainThread.getApplicationThread(), 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().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntent(
+                    mMainThread.getApplicationThread(), 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().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntent(
+                    mMainThread.getApplicationThread(), 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().broadcastIntentWithFeature(
-                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
+            ActivityManager.getService().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermissions, appOp,
                     options, true, false, getUserId());
         } catch (RemoteException e) {
@@ -1277,10 +1277,9 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
-                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
-                    user.getIdentifier());
+            ActivityManager.getService().broadcastIntent(mMainThread.getApplicationThread(),
+                    intent, resolvedType, null, Activity.RESULT_OK, null, null, null,
+                    AppOpsManager.OP_NONE, null, false, false, user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1300,8 +1299,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntent(
+                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, AppOpsManager.OP_NONE,
                     options, false, false, user.getIdentifier());
         } catch (RemoteException e) {
@@ -1317,8 +1316,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntentWithFeature(
-                    mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntent(
+                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                     Activity.RESULT_OK, null, null, receiverPermissions, appOp, null, false, false,
                     user.getIdentifier());
         } catch (RemoteException e) {
@@ -1368,8 +1367,8 @@
                 : new String[] {receiverPermission};
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntentWithFeature(
-                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
+            ActivityManager.getService().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermissions,
                     appOp, options, true, false, user.getIdentifier());
         } catch (RemoteException e) {
@@ -1409,8 +1408,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntentWithFeature(
-                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
                 getUserId());
         } catch (RemoteException e) {
@@ -1445,8 +1444,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntentWithFeature(
-                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
+            ActivityManager.getService().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
                     AppOpsManager.OP_NONE, null, true, true, getUserId());
         } catch (RemoteException e) {
@@ -1477,8 +1476,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntentWithFeature(
-                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, true,
                     user.getIdentifier());
         } catch (RemoteException e) {
@@ -1492,8 +1491,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntentWithFeature(
-                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, null,
+            ActivityManager.getService().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options, false, true,
                 user.getIdentifier());
         } catch (RemoteException e) {
@@ -1527,8 +1526,8 @@
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
             intent.prepareToLeaveProcess(this);
-            ActivityManager.getService().broadcastIntentWithFeature(
-                mMainThread.getApplicationThread(), getFeatureId(), intent, resolvedType, rd,
+            ActivityManager.getService().broadcastIntent(
+                mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
                     AppOpsManager.OP_NONE, null, true, true, user.getIdentifier());
         } catch (RemoteException e) {
@@ -1613,9 +1612,9 @@
             }
         }
         try {
-            final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
-                    mMainThread.getApplicationThread(), mBasePackageName, getFeatureId(), rd,
-                    filter, broadcastPermission, userId, flags);
+            final Intent intent = ActivityManager.getService().registerReceiver(
+                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
+                    broadcastPermission, userId, flags);
             if (intent != null) {
                 intent.setExtrasClassLoader(getClassLoader());
                 intent.prepareToEnterProcess();
@@ -1688,9 +1687,9 @@
             validateServiceIntent(service);
             service.prepareToLeaveProcess(this);
             ComponentName cn = ActivityManager.getService().startService(
-                    mMainThread.getApplicationThread(), service,
-                    service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
-                    getOpPackageName(), getFeatureId(), user.getIdentifier());
+                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
+                            getContentResolver()), requireForeground,
+                            getOpPackageName(), 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 83fa9d7..cb6a476 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -104,38 +104,25 @@
     // Special low-level communication with activity manager.
     void handleApplicationCrash(in IBinder app,
             in ApplicationErrorReport.ParcelableCrashInfo crashInfo);
-    /** @deprecated Use {@link #startActivityWithFeature} instead */
-    @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link android.content.Context#startActivity(android.content.Intent)} instead")
+    @UnsupportedAppUsage
     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(maxTargetSdk=29, publicAlternatives="Use {@link android.content.Context#registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter)} instead")
+    @UnsupportedAppUsage
     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);
-    /** @deprecated Use {@link #broadcastIntentWithFeature} instead */
-    @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link android.content.Context#sendBroadcast(android.content.Intent)} instead")
+    @UnsupportedAppUsage
     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,
@@ -158,8 +145,7 @@
     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,
-            in String callingFeatureId, int userId);
+            in String resolvedType, boolean requireForeground, in String callingPackage, int userId);
     @UnsupportedAppUsage
     int stopService(in IApplicationThread caller, in Intent service,
             in String resolvedType, int userId);
@@ -240,14 +226,10 @@
     ParceledListSlice getRecentTasks(int maxNum, int flags, int userId);
     @UnsupportedAppUsage
     oneway void serviceDoneExecuting(in IBinder token, int type, int startId, int res);
-    /** @deprecated  Use {@link #getIntentSenderWithFeature} instead */
-    @UnsupportedAppUsage(maxTargetSdk=29, publicAlternatives="Use {@link PendingIntent#getIntentSender()} instead")
+    @UnsupportedAppUsage
     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);
@@ -373,16 +355,11 @@
     boolean isIntentSenderAnActivity(in IIntentSender sender);
     boolean isIntentSenderAForegroundService(in IIntentSender sender);
     boolean isIntentSenderABroadcast(in IIntentSender sender);
-    /** @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")
+    @UnsupportedAppUsage
     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 180507c..be2f144 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -85,17 +85,16 @@
  * {@hide}
  */
 interface IActivityTaskManager {
-    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 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 startActivities(in IApplicationThread caller, in String callingPackage,
-            in String callingFeatureId, in Intent[] intents, in String[] resolvedTypes,
-            in IBinder resultTo, in Bundle options, int userId);
+            in Intent[] intents, in String[] resolvedTypes, in IBinder resultTo,
+            in Bundle options, int userId);
     int startActivityAsUser(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);
+            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,
@@ -103,19 +102,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 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);
+            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 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);
+            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);
     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 f41d705..3ce7190 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, String callingFeatureId,
+    int startActivity(IBinder whoThread, String callingPackage,
             in Intent intent, String resolvedType, in Bundle options);
     void setExcludeFromRecents(boolean exclude);
 }
diff --git a/core/java/android/app/ITaskOrganizerController.aidl b/core/java/android/app/ITaskOrganizerController.aidl
index 5d5956e..9d6c3d6 100644
--- a/core/java/android/app/ITaskOrganizerController.aidl
+++ b/core/java/android/app/ITaskOrganizerController.aidl
@@ -52,7 +52,11 @@
     boolean deleteRootTask(IWindowContainer task);
 
     /** Gets direct child tasks (ordered from top-to-bottom) */
-    List<ActivityManager.RunningTaskInfo> getChildTasks(in IWindowContainer parent);
+    List<ActivityManager.RunningTaskInfo> getChildTasks(in IWindowContainer parent,
+            in int[] activityTypes);
+
+    /** Gets all root tasks on a display (ordered from top-to-bottom) */
+    List<ActivityManager.RunningTaskInfo> getRootTasks(int displayId, in int[] activityTypes);
 
     /** Get the root task which contains the current ime target */
     IWindowContainer getImeTarget(int display);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 18932c6..62c905d 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(), who.getFeatureId(), intent,
+                .startActivity(whoThread, who.getBasePackageName(), 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(), who.getFeatureId(), intents,
-                        resolvedTypes, token, options, userId);
+                .startActivities(whoThread, who.getBasePackageName(), 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(), who.getFeatureId(), intent,
+                .startActivity(whoThread, who.getBasePackageName(), 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(), who.getFeatureId(),
-                        intent, intent.resolveTypeIfNeeded(who.getContentResolver()),
+                .startActivityAsUser(whoThread, who.getBasePackageName(), intent,
+                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                         token, resultWho,
                         requestCode, 0, null, options, user.getIdentifier());
             checkStartActivityResult(result, intent);
@@ -2022,8 +2022,7 @@
             intent.migrateExtraStreamToClipData();
             intent.prepareToLeaveProcess(who);
             int result = appTask.startActivity(whoThread.asBinder(), who.getBasePackageName(),
-                    who.getFeatureId(), intent,
-                    intent.resolveTypeIfNeeded(who.getContentResolver()), options);
+                    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 f68c929..b8348c7 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().getIntentSenderWithFeature(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
+                ActivityManager.getService().getIntentSender(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
                     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().getIntentSenderWithFeature(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
+                ActivityManager.getService().getIntentSender(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
                     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().getIntentSenderWithFeature(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
+                ActivityManager.getService().getIntentSender(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
                     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().getIntentSenderWithFeature(
-                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName, context.getFeatureId(),
+                ActivityManager.getService().getIntentSender(
+                    ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
                     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().getIntentSenderWithFeature(
-                    ActivityManager.INTENT_SENDER_BROADCAST, packageName, context.getFeatureId(),
+                ActivityManager.getService().getIntentSender(
+                    ActivityManager.INTENT_SENDER_BROADCAST, packageName,
                     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().getIntentSenderWithFeature(
-                    serviceKind, packageName, context.getFeatureId(),
+                ActivityManager.getService().getIntentSender(
+                    serviceKind, packageName,
                     null, null, requestCode, new Intent[] { intent },
                     resolvedType != null ? new String[] { resolvedType } : null,
                     flags, null, context.getUserId());
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index bfd966c5..67d94de 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -247,6 +247,14 @@
         return mSourceRectHint != null && !mSourceRectHint.isEmpty();
     }
 
+    /**
+     * @return True if no parameters are set
+     * @hide
+     */
+    public boolean empty() {
+        return !hasSourceBoundsHint() && !hasSetActions() && !hasSetAspectRatio();
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index 662ca6e..f7d712d 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -149,6 +149,13 @@
     public IWindowContainer token;
 
     /**
+     * The PictureInPictureParams for the Task, if set.
+     * @hide
+     */
+    @Nullable
+    public PictureInPictureParams pictureInPictureParams;
+
+    /**
      * The activity type of the top activity in this task.
      * @hide
      */
@@ -209,6 +216,9 @@
         configuration.readFromParcel(source);
         token = IWindowContainer.Stub.asInterface(source.readStrongBinder());
         topActivityType = source.readInt();
+        pictureInPictureParams = source.readInt() != 0
+            ? PictureInPictureParams.CREATOR.createFromParcel(source)
+            : null;
     }
 
     /**
@@ -246,6 +256,12 @@
         configuration.writeToParcel(dest, flags);
         dest.writeStrongInterface(token);
         dest.writeInt(topActivityType);
+        if (pictureInPictureParams == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+            pictureInPictureParams.writeToParcel(dest, flags);
+        }
     }
 
     @Override
@@ -261,6 +277,7 @@
                 + " supportsSplitScreenMultiWindow=" + supportsSplitScreenMultiWindow
                 + " resizeMode=" + resizeMode
                 + " token=" + token
-                + " topActivityType=" + topActivityType;
+                + " topActivityType=" + topActivityType
+                + " pictureInPictureParams=" + pictureInPictureParams;
     }
 }
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 6f1effd..5f74d2e 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -598,7 +598,8 @@
      *     is not able to access the wallpaper.
      */
     public Drawable getDrawable() {
-        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, mCmProxy);
+        final ColorManagementProxy cmProxy = getColorManagementProxy();
+        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, cmProxy);
         if (bm != null) {
             Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
             dr.setDither(false);
@@ -829,7 +830,8 @@
      * null pointer if these is none.
      */
     public Drawable peekDrawable() {
-        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, mCmProxy);
+        final ColorManagementProxy cmProxy = getColorManagementProxy();
+        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, cmProxy);
         if (bm != null) {
             Drawable dr = new BitmapDrawable(mContext.getResources(), bm);
             dr.setDither(false);
@@ -853,7 +855,8 @@
      */
     @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
     public Drawable getFastDrawable() {
-        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, mCmProxy);
+        final ColorManagementProxy cmProxy = getColorManagementProxy();
+        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, cmProxy);
         if (bm != null) {
             return new FastBitmapDrawable(bm);
         }
@@ -869,7 +872,8 @@
      */
     @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE)
     public Drawable peekFastDrawable() {
-        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, mCmProxy);
+        final ColorManagementProxy cmProxy = getColorManagementProxy();
+        Bitmap bm = sGlobals.peekWallpaperBitmap(mContext, false, FLAG_SYSTEM, cmProxy);
         if (bm != null) {
             return new FastBitmapDrawable(bm);
         }
@@ -892,10 +896,11 @@
         if (!shouldEnableWideColorGamut()) {
             return false;
         }
-        Bitmap bitmap = sGlobals.peekWallpaperBitmap(mContext, false, which, mCmProxy);
+        final ColorManagementProxy cmProxy = getColorManagementProxy();
+        Bitmap bitmap = sGlobals.peekWallpaperBitmap(mContext, false, which, cmProxy);
         return bitmap != null && bitmap.getColorSpace() != null
                 && bitmap.getColorSpace() != ColorSpace.get(ColorSpace.Named.SRGB)
-                && mCmProxy.isSupportedColorSpace(bitmap.getColorSpace());
+                && cmProxy.isSupportedColorSpace(bitmap.getColorSpace());
     }
 
     /**
@@ -928,8 +933,8 @@
      * @hide
      */
     public Bitmap getBitmapAsUser(int userId, boolean hardware) {
-        return sGlobals.peekWallpaperBitmap(
-                mContext, true, FLAG_SYSTEM, userId, hardware, mCmProxy);
+        final ColorManagementProxy cmProxy = getColorManagementProxy();
+        return sGlobals.peekWallpaperBitmap(mContext, true, FLAG_SYSTEM, userId, hardware, cmProxy);
     }
 
     /**
@@ -2074,12 +2079,23 @@
     }
 
     /**
-     * A private class to help Globals#getCurrentWallpaperLocked handle color management.
+     * Get the instance of {@link ColorManagementProxy}.
+     *
+     * @return instance of {@link ColorManagementProxy}.
+     * @hide
      */
-    private static class ColorManagementProxy {
+    public ColorManagementProxy getColorManagementProxy() {
+        return mCmProxy;
+    }
+
+    /**
+     * A hidden class to help {@link Globals#getCurrentWallpaperLocked} handle color management.
+     * @hide
+     */
+    public static class ColorManagementProxy {
         private final Set<ColorSpace> mSupportedColorSpaces = new HashSet<>();
 
-        ColorManagementProxy(Context context) {
+        public ColorManagementProxy(@NonNull Context context) {
             // Get a list of supported wide gamut color spaces.
             Display display = context.getDisplay();
             if (display != null) {
@@ -2087,9 +2103,14 @@
             }
         }
 
+        @NonNull
+        public Set<ColorSpace> getSupportedColorSpaces() {
+            return mSupportedColorSpaces;
+        }
+
         boolean isSupportedColorSpace(ColorSpace colorSpace) {
             return colorSpace != null && (colorSpace == ColorSpace.get(ColorSpace.Named.SRGB)
-                    || mSupportedColorSpaces.contains(colorSpace));
+                    || getSupportedColorSpaces().contains(colorSpace));
         }
 
         void doColorManagement(ImageDecoder decoder, ImageDecoder.ImageInfo info) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7599791..dc15b51 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8951,7 +8951,8 @@
      *
      * <strong>Note: Starting from Android R, apps should no longer call this method with the
      * setting {@link android.provider.Settings.Secure#LOCATION_MODE}, which is deprecated. Instead,
-     * device owners should call {@link #setLocationEnabled(ComponentName, boolean)}.
+     * device owners should call {@link #setLocationEnabled(ComponentName, boolean)}. This will be
+     * enforced for all apps targeting Android R or above.
      * </strong>
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
@@ -8961,6 +8962,7 @@
      */
     public void setSecureSetting(@NonNull ComponentName admin, String setting, String value) {
         throwIfParentInstance("setSecureSetting");
+
         if (mService != null) {
             try {
                 mService.setSecureSetting(admin, setting, value);
@@ -9462,16 +9464,6 @@
      * {@link android.os.Build.VERSION_CODES#M} the app-op matching the permission is set to
      * {@link android.app.AppOpsManager#MODE_IGNORED}, but the permission stays granted.
      *
-     * NOTE: Starting from Android R, location-related permissions cannot be granted by the
-     * admin: Calling this method with {@link #PERMISSION_GRANT_STATE_GRANTED} for any of the
-     * following permissions will return false:
-     *
-     * <ul>
-     * <li>{@code ACCESS_FINE_LOCATION}</li>
-     * <li>{@code ACCESS_BACKGROUND_LOCATION}</li>
-     * <li>{@code ACCESS_COARSE_LOCATION}</li>
-     * </ul>
-     *
      * @param admin Which profile or device owner this request is associated with.
      * @param packageName The application to grant or revoke a permission to.
      * @param permission The permission to grant or revoke.
diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java
index 5b36789..e289a27 100644
--- a/core/java/android/app/compat/CompatChanges.java
+++ b/core/java/android/app/compat/CompatChanges.java
@@ -20,6 +20,7 @@
 import android.annotation.SystemApi;
 import android.compat.Compatibility;
 import android.content.Context;
+import android.os.Binder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -70,11 +71,14 @@
             @NonNull UserHandle user) {
         IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+        final long token = Binder.clearCallingIdentity();
         try {
             return platformCompat.isChangeEnabledByPackageName(changeId, packageName,
                     user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
     }
 
@@ -99,10 +103,13 @@
     public static boolean isChangeEnabled(long changeId, int uid) {
         IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(
                 ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+        final long token = Binder.clearCallingIdentity();
         try {
             return platformCompat.isChangeEnabledByUid(changeId, uid);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
     }
 }
diff --git a/core/java/android/app/compat/TEST_MAPPING b/core/java/android/app/compat/TEST_MAPPING
new file mode 100644
index 0000000..c047df5
--- /dev/null
+++ b/core/java/android/app/compat/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+    "imports": [
+        {
+            "path": "frameworks/base/services/core/java/com/android/services/compat"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index ee2cc6d..ab49230 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -66,7 +66,7 @@
      */
     @SystemApi
     @SuppressLint("ActionValue")
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public static final String ACTION_CONNECTION_STATE_CHANGED =
             "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
 
@@ -296,7 +296,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -345,7 +345,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
         if (VDBG) log("getConnectionPolicy(" + device + ")");
         final IBluetoothA2dpSink service = getService();
@@ -370,7 +370,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean isAudioPlaying(@Nullable BluetoothDevice device) {
         final IBluetoothA2dpSink service = getService();
         if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 01ccb86..66bfcbd 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -26,6 +26,7 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.app.ActivityThread;
+import android.app.PropertyInvalidatedCache;
 import android.bluetooth.BluetoothProfile.ConnectionPolicy;
 import android.bluetooth.le.BluetoothLeAdvertiser;
 import android.bluetooth.le.BluetoothLeScanner;
@@ -994,6 +995,37 @@
         return false;
     }
 
+    private static final String BLUETOOTH_GET_STATE_CACHE_PROPERTY = "cache_key.bluetooth.get_state";
+
+    private final PropertyInvalidatedCache<Void, Integer> mBluetoothGetStateCache =
+            new PropertyInvalidatedCache<Void, Integer>(
+                8, BLUETOOTH_GET_STATE_CACHE_PROPERTY) {
+                @Override
+                protected Integer recompute(Void query) {
+                    try {
+                        mServiceLock.readLock().lock();
+                        if (mService != null) {
+                            return mService.getState();
+                        }
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "", e);
+                    } finally {
+                        mServiceLock.readLock().unlock();
+                    }
+                    return BluetoothAdapter.STATE_OFF;
+                }
+            };
+
+    /** @hide */
+    public void disableBluetoothGetStateCache() {
+        mBluetoothGetStateCache.disableLocal();
+    }
+
+    /** @hide */
+    public static void invalidateBluetoothGetStateCache() {
+        PropertyInvalidatedCache.invalidateCache(BLUETOOTH_GET_STATE_CACHE_PROPERTY);
+    }
+
     /**
      * Get the current state of the local Bluetooth adapter.
      * <p>Possible return values are
@@ -1007,18 +1039,7 @@
     @RequiresPermission(Manifest.permission.BLUETOOTH)
     @AdapterState
     public int getState() {
-        int state = BluetoothAdapter.STATE_OFF;
-
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                state = mService.getState();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
+        int state = mBluetoothGetStateCache.query(null);
 
         // Consider all internal states as OFF
         if (state == BluetoothAdapter.STATE_BLE_ON || state == BluetoothAdapter.STATE_BLE_TURNING_ON
@@ -1056,18 +1077,7 @@
     @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine "
             + "whether you can use BLE & BT classic.")
     public int getLeState() {
-        int state = BluetoothAdapter.STATE_OFF;
-
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                state = mService.getState();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
+        int state = mBluetoothGetStateCache.query(null);
 
         if (VDBG) {
             Log.d(TAG, "getLeState() returning " + BluetoothAdapter.nameForState(state));
@@ -1960,6 +1970,38 @@
         }
     }
 
+    private static final String BLUETOOTH_FILTERING_CACHE_PROPERTY =
+            "cache_key.bluetooth.is_offloaded_filtering_supported";
+    private final PropertyInvalidatedCache<Void, Boolean> mBluetoothFilteringCache =
+            new PropertyInvalidatedCache<Void, Boolean>(
+                8, BLUETOOTH_FILTERING_CACHE_PROPERTY) {
+                @Override
+                protected Boolean recompute(Void query) {
+                    try {
+                        mServiceLock.readLock().lock();
+                        if (mService != null) {
+                            return mService.isOffloadedFilteringSupported();
+                        }
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e);
+                    } finally {
+                        mServiceLock.readLock().unlock();
+                    }
+                    return false;
+
+                }
+            };
+
+    /** @hide */
+    public void disableIsOffloadedFilteringSupportedCache() {
+        mBluetoothFilteringCache.disableLocal();
+    }
+
+    /** @hide */
+    public static void invalidateIsOffloadedFilteringSupportedCache() {
+        PropertyInvalidatedCache.invalidateCache(BLUETOOTH_FILTERING_CACHE_PROPERTY);
+    }
+
     /**
      * Return true if offloaded filters are supported
      *
@@ -1969,17 +2011,7 @@
         if (!getLeAccess()) {
             return false;
         }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.isOffloadedFilteringSupported();
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get isOffloadedFilteringSupported, error: ", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return false;
+        return mBluetoothFilteringCache.query(null);
     }
 
     /**
@@ -2351,6 +2383,43 @@
         return BluetoothAdapter.STATE_DISCONNECTED;
     }
 
+    private static final String BLUETOOTH_PROFILE_CACHE_PROPERTY =
+            "cache_key.bluetooth.get_profile_connection_state";
+    private final PropertyInvalidatedCache<Integer, Integer>
+            mGetProfileConnectionStateCache =
+            new PropertyInvalidatedCache<Integer, Integer>(
+                8, BLUETOOTH_PROFILE_CACHE_PROPERTY) {
+                @Override
+                protected Integer recompute(Integer query) {
+                    try {
+                        mServiceLock.readLock().lock();
+                        if (mService != null) {
+                            return mService.getProfileConnectionState(query);
+                        }
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "getProfileConnectionState:", e);
+                    } finally {
+                        mServiceLock.readLock().unlock();
+                    }
+                    return BluetoothProfile.STATE_DISCONNECTED;
+                }
+                @Override
+                public String queryToString(Integer query) {
+                    return String.format("getProfileConnectionState(profile=\"%d\")",
+                                         query);
+                }
+            };
+
+    /** @hide */
+    public void disableGetProfileConnectionStateCache() {
+        mGetProfileConnectionStateCache.disableLocal();
+    }
+
+    /** @hide */
+    public static void invalidateGetProfileConnectionStateCache() {
+        PropertyInvalidatedCache.invalidateCache(BLUETOOTH_PROFILE_CACHE_PROPERTY);
+    }
+
     /**
      * Get the current connection state of a profile.
      * This function can be used to check whether the local Bluetooth adapter
@@ -2368,17 +2437,7 @@
         if (getState() != STATE_ON) {
             return BluetoothProfile.STATE_DISCONNECTED;
         }
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.getProfileConnectionState(profile);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "getProfileConnectionState:", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-        return BluetoothProfile.STATE_DISCONNECTED;
+        return mGetProfileConnectionStateCache.query(new Integer(profile));
     }
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index e07ca52..1f89ddf 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -38,9 +38,6 @@
 import java.util.List;
 
 /**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- *
  * Public API for controlling the Bluetooth Pbap Service. This includes
  * Bluetooth Phone book Access profile.
  * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap
@@ -56,6 +53,11 @@
  * notification when it is bound, this is especially important if you wish to
  * immediately call methods on BluetoothPbap after construction.
  *
+ * To get an instance of the BluetoothPbap class, you can call
+ * {@link BluetoothAdapter#getProfileProxy(Context, ServiceListener, int)} with the final param
+ * being {@link BluetoothProfile#PBAP}. The ServiceListener should be able to get the instance of
+ * BluetoothPbap in {@link android.bluetooth.BluetoothProfile.ServiceListener#onServiceConnected}.
+ *
  * Android only supports one connected Bluetooth Pce at a time.
  *
  * @hide
@@ -87,6 +89,7 @@
      */
     @SuppressLint("ActionValue")
     @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_CONNECTION_STATE_CHANGED =
             "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
@@ -235,7 +238,8 @@
      */
     @SystemApi
     @Override
-    public int getConnectionState(@Nullable BluetoothDevice device) {
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public @BtProfileState int getConnectionState(@Nullable BluetoothDevice device) {
         log("getConnectionState: device=" + device);
         try {
             final IBluetoothPbap service = mService;
@@ -287,7 +291,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 0e0161f..f32a4ab 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -700,27 +700,6 @@
     /** @hide */
     public static final String REMOTE_CALLBACK_RESULT = "result";
 
-    /**
-     * How long we wait for an attached process to publish its content providers
-     * before we decide it must be hung.
-     * @hide
-     */
-    public static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS = 10 * 1000;
-
-    /**
-     * How long we wait for an provider to be published. Should be longer than
-     * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS}.
-     * @hide
-     */
-    public static final int CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS =
-            CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS + 10 * 1000;
-
-    // Should be >= {@link #CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS}, because that's how
-    // long ActivityManagerService is giving a content provider to get published if a new process
-    // needs to be started for that.
-    private static final int GET_TYPE_TIMEOUT_MILLIS =
-            CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS + 5 * 1000;
-
     public ContentResolver(@Nullable Context context) {
         this(context, null);
     }
@@ -870,6 +849,8 @@
         }
     }
 
+    private static final int GET_TYPE_TIMEOUT_MILLIS = 3000;
+
     private static class GetTypeResultListener implements RemoteCallback.OnResultListener {
         @GuardedBy("this")
         public boolean done;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6f8a99f..0f88c90 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4289,7 +4289,9 @@
      * intent filter in their manifests, so that they can be looked up and bound to by
      * {@code DataLoaderManagerService}.
      *
-     * Data loader service providers must be privileged apps. 
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     *
+     * Data loader service providers must be privileged apps.
      * See {@link com.android.server.pm.PackageManagerShellCommandDataLoader} as an example of such
      * data loader service provider.
      *
@@ -4970,11 +4972,14 @@
      * <pre>
      * &lt;accessibility-shortcut-target
      *     android:description="@string/shortcut_target_description"
-     *     android:summary="@string/shortcut_target_summary" /&gt;
+     *     android:summary="@string/shortcut_target_summary"
+     *     android:animatedImageDrawable="@drawable/shortcut_target_animated_image"
+     *     android:htmlDescription="@string/shortcut_target_html_description" /&gt;
      * </pre>
      * <p>
      * Both description and summary are necessary. The system will ignore the accessibility
-     * shortcut target if they are missing.
+     * shortcut target if they are missing. The animated image and html description are supported
+     * to help users understand how to use the shortcut target.
      * </p>
      */
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
diff --git a/core/java/android/content/integrity/AppInstallMetadata.java b/core/java/android/content/integrity/AppInstallMetadata.java
index 351edc9..cd5117b 100644
--- a/core/java/android/content/integrity/AppInstallMetadata.java
+++ b/core/java/android/content/integrity/AppInstallMetadata.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -33,18 +34,18 @@
 public final class AppInstallMetadata {
     private final String mPackageName;
     // Raw string encoding for the SHA-256 hash of the certificate of the app.
-    private final String mAppCertificate;
+    private final List<String> mAppCertificates;
     private final String mInstallerName;
     // Raw string encoding for the SHA-256 hash of the certificate of the installer.
-    private final String mInstallerCertificate;
+    private final List<String> mInstallerCertificates;
     private final long mVersionCode;
     private final boolean mIsPreInstalled;
 
     private AppInstallMetadata(Builder builder) {
         this.mPackageName = builder.mPackageName;
-        this.mAppCertificate = builder.mAppCertificate;
+        this.mAppCertificates = builder.mAppCertificates;
         this.mInstallerName = builder.mInstallerName;
-        this.mInstallerCertificate = builder.mInstallerCertificate;
+        this.mInstallerCertificates = builder.mInstallerCertificates;
         this.mVersionCode = builder.mVersionCode;
         this.mIsPreInstalled = builder.mIsPreInstalled;
     }
@@ -55,8 +56,8 @@
     }
 
     @NonNull
-    public String getAppCertificate() {
-        return mAppCertificate;
+    public List<String> getAppCertificates() {
+        return mAppCertificates;
     }
 
     @NonNull
@@ -65,8 +66,8 @@
     }
 
     @NonNull
-    public String getInstallerCertificate() {
-        return mInstallerCertificate;
+    public List<String> getInstallerCertificates() {
+        return mInstallerCertificates;
     }
 
     /** @see AppInstallMetadata.Builder#setVersionCode(long) */
@@ -82,12 +83,12 @@
     @Override
     public String toString() {
         return String.format(
-                "AppInstallMetadata { PackageName = %s, AppCert = %s, InstallerName = %s,"
-                    + " InstallerCert = %s, VersionCode = %d, PreInstalled = %b }",
+                "AppInstallMetadata { PackageName = %s, AppCerts = %s, InstallerName = %s,"
+                    + " InstallerCerts = %s, VersionCode = %d, PreInstalled = %b }",
                 mPackageName,
-                mAppCertificate,
+                mAppCertificates,
                 mInstallerName == null ? "null" : mInstallerName,
-                mInstallerCertificate == null ? "null" : mInstallerCertificate,
+                mInstallerCertificates == null ? "null" : mInstallerCertificates,
                 mVersionCode,
                 mIsPreInstalled);
     }
@@ -95,9 +96,9 @@
     /** Builder class for constructing {@link AppInstallMetadata} objects. */
     public static final class Builder {
         private String mPackageName;
-        private String mAppCertificate;
+        private List<String> mAppCertificates;
         private String mInstallerName;
-        private String mInstallerCertificate;
+        private List<String> mInstallerCertificates;
         private long mVersionCode;
         private boolean mIsPreInstalled;
 
@@ -118,11 +119,11 @@
          * <p>It is represented as the raw string encoding for the SHA-256 hash of the certificate
          * of the app.
          *
-         * @see AppInstallMetadata#getAppCertificate()
+         * @see AppInstallMetadata#getAppCertificates()
          */
         @NonNull
-        public Builder setAppCertificate(@NonNull String appCertificate) {
-            this.mAppCertificate = Objects.requireNonNull(appCertificate);
+        public Builder setAppCertificates(@NonNull List<String> appCertificates) {
+            this.mAppCertificates = Objects.requireNonNull(appCertificates);
             return this;
         }
 
@@ -143,11 +144,11 @@
          * <p>It is represented as the raw string encoding for the SHA-256 hash of the certificate
          * of the installer.
          *
-         * @see AppInstallMetadata#getInstallerCertificate()
+         * @see AppInstallMetadata#getInstallerCertificates()
          */
         @NonNull
-        public Builder setInstallerCertificate(@NonNull String installerCertificate) {
-            this.mInstallerCertificate = Objects.requireNonNull(installerCertificate);
+        public Builder setInstallerCertificates(@NonNull List<String> installerCertificates) {
+            this.mInstallerCertificates = Objects.requireNonNull(installerCertificates);
             return this;
         }
 
@@ -181,7 +182,7 @@
         @NonNull
         public AppInstallMetadata build() {
             Objects.requireNonNull(mPackageName);
-            Objects.requireNonNull(mAppCertificate);
+            Objects.requireNonNull(mAppCertificates);
             return new AppInstallMetadata(this);
         }
     }
diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java
index d25f413..42459779 100644
--- a/core/java/android/content/integrity/AtomicFormula.java
+++ b/core/java/android/content/integrity/AtomicFormula.java
@@ -30,6 +30,8 @@
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.util.Collections;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -387,7 +389,7 @@
             if (mValue == null || mIsHashedValue == null) {
                 return false;
             }
-            return getStringMetadataValue(appInstallMetadata, getKey()).equals(mValue);
+            return getMetadataValue(appInstallMetadata, getKey()).contains(mValue);
         }
 
         @Override
@@ -448,17 +450,17 @@
             return mIsHashedValue;
         }
 
-        private static String getStringMetadataValue(
+        private static List<String> getMetadataValue(
                 AppInstallMetadata appInstallMetadata, int key) {
             switch (key) {
                 case AtomicFormula.PACKAGE_NAME:
-                    return appInstallMetadata.getPackageName();
+                    return Collections.singletonList(appInstallMetadata.getPackageName());
                 case AtomicFormula.APP_CERTIFICATE:
-                    return appInstallMetadata.getAppCertificate();
+                    return appInstallMetadata.getAppCertificates();
                 case AtomicFormula.INSTALLER_CERTIFICATE:
-                    return appInstallMetadata.getInstallerCertificate();
+                    return appInstallMetadata.getInstallerCertificates();
                 case AtomicFormula.INSTALLER_NAME:
-                    return appInstallMetadata.getInstallerName();
+                    return Collections.singletonList(appInstallMetadata.getInstallerName());
                 default:
                     throw new IllegalStateException(
                             "Unexpected key in StringAtomicFormula: " + key);
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 2ba2840..edc20d9 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -87,7 +87,6 @@
             mService.startActivityAsUser(
                     mContext.getIApplicationThread(),
                     mContext.getPackageName(),
-                    mContext.getFeatureId(),
                     component,
                     targetUser.getIdentifier(),
                     true);
@@ -115,7 +114,6 @@
             mService.startActivityAsUserByIntent(
                     mContext.getIApplicationThread(),
                     mContext.getPackageName(),
-                    mContext.getFeatureId(),
                     intent,
                     targetUser.getIdentifier());
         } catch (RemoteException ex) {
@@ -141,8 +139,7 @@
     public void startActivity(@NonNull ComponentName component, @NonNull UserHandle targetUser) {
         try {
             mService.startActivityAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(), mContext.getFeatureId(), component,
-                    targetUser.getIdentifier(), false);
+                    mContext.getPackageName(), component, targetUser.getIdentifier(), false);
         } catch (RemoteException ex) {
             throw ex.rethrowFromSystemServer();
         }
diff --git a/core/java/android/content/pm/ICrossProfileApps.aidl b/core/java/android/content/pm/ICrossProfileApps.aidl
index 98bf2dd..a69b988 100644
--- a/core/java/android/content/pm/ICrossProfileApps.aidl
+++ b/core/java/android/content/pm/ICrossProfileApps.aidl
@@ -28,14 +28,13 @@
  */
 interface ICrossProfileApps {
     void startActivityAsUser(in IApplicationThread caller, in String callingPackage,
-            in String callingFeatureId, in ComponentName component, int userId,
-            boolean launchMainActivity);
+            in ComponentName component, int userId, boolean launchMainActivity);
     void startActivityAsUserByIntent(in IApplicationThread caller, in String callingPackage,
-            in String callingFeatureId, in Intent intent, int userId);
+                        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 b5f4f80..38a9ac4a 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,
-                String callingFeatureId, in PackageInstaller.SessionInfo sessionInfo,
-                in Rect sourceBounds, in Bundle opts, in UserHandle user);
+                in PackageInstaller.SessionInfo sessionInfo, in Rect sourceBounds, in Bundle opts,
+                in UserHandle user);
     void startActivityAsUser(in IApplicationThread caller, String callingPackage,
-            String callingFeatureId, in ComponentName component, in Rect sourceBounds,
+            in ComponentName component, in Rect sourceBounds,
             in Bundle opts, in UserHandle user);
-    void showAppDetailsAsUser(in IApplicationThread caller, String callingPackage,
-            String callingFeatureId, in ComponentName component, in Rect sourceBounds,
+    void showAppDetailsAsUser(in IApplicationThread caller,
+            String callingPackage, 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 featureId, String id,
+    boolean startShortcut(String callingPackage, String packageName, 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/InstallationFile.java b/core/java/android/content/pm/InstallationFile.java
index 111ad32..b449945 100644
--- a/core/java/android/content/pm/InstallationFile.java
+++ b/core/java/android/content/pm/InstallationFile.java
@@ -16,82 +16,59 @@
 
 package android.content.pm;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
 /**
  * Defines the properties of a file in an installation session.
- * TODO(b/136132412): update with new APIs.
- *
  * @hide
  */
 @SystemApi
 public final class InstallationFile implements Parcelable {
-    public static final int FILE_TYPE_UNKNOWN = -1;
-    public static final int FILE_TYPE_APK = 0;
-    public static final int FILE_TYPE_LIB = 1;
-    public static final int FILE_TYPE_OBB = 2;
+    private final @PackageInstaller.FileLocation int mLocation;
+    private final @NonNull String mName;
+    private final long mLengthBytes;
+    private final @Nullable byte[] mMetadata;
+    private final @Nullable byte[] mSignature;
 
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"FILE_TYPE_"}, value = {
-            FILE_TYPE_APK,
-            FILE_TYPE_LIB,
-            FILE_TYPE_OBB,
-    })
-    public @interface FileType {
-    }
-
-    private String mFileName;
-    private @FileType int mFileType;
-    private long mFileSize;
-    private byte[] mMetadata;
-
-    public InstallationFile(@NonNull String fileName, long fileSize,
-            @Nullable byte[] metadata) {
-        mFileName = fileName;
-        mFileSize = fileSize;
+    public InstallationFile(@PackageInstaller.FileLocation int location, @NonNull String name,
+            long lengthBytes, @Nullable byte[] metadata, @Nullable byte[] signature) {
+        mLocation = location;
+        mName = name;
+        mLengthBytes = lengthBytes;
         mMetadata = metadata;
-        if (fileName.toLowerCase().endsWith(".apk")) {
-            mFileType = FILE_TYPE_APK;
-        } else if (fileName.toLowerCase().endsWith(".obb")) {
-            mFileType = FILE_TYPE_OBB;
-        } else if (fileName.toLowerCase().endsWith(".so") && fileName.toLowerCase().startsWith(
-                "lib/")) {
-            mFileType = FILE_TYPE_LIB;
-        } else {
-            mFileType = FILE_TYPE_UNKNOWN;
-        }
+        mSignature = signature;
     }
 
-    public @FileType int getFileType() {
-        return mFileType;
+    public @PackageInstaller.FileLocation int getLocation() {
+        return mLocation;
     }
 
     public @NonNull String getName() {
-        return mFileName;
+        return mName;
     }
 
-    public long getSize() {
-        return mFileSize;
+    public long getLengthBytes() {
+        return mLengthBytes;
     }
 
     public @Nullable byte[] getMetadata() {
         return mMetadata;
     }
 
+    public @Nullable byte[] getSignature() {
+        return mSignature;
+    }
+
     private InstallationFile(Parcel source) {
-        mFileName = source.readString();
-        mFileType = source.readInt();
-        mFileSize = source.readLong();
+        mLocation = source.readInt();
+        mName = source.readString();
+        mLengthBytes = source.readLong();
         mMetadata = source.createByteArray();
+        mSignature = source.createByteArray();
     }
 
     @Override
@@ -101,10 +78,11 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeString(mFileName);
-        dest.writeInt(mFileType);
-        dest.writeLong(mFileSize);
+        dest.writeInt(mLocation);
+        dest.writeString(mName);
+        dest.writeLong(mLengthBytes);
         dest.writeByteArray(mMetadata);
+        dest.writeByteArray(mSignature);
     }
 
     public static final @NonNull Creator<InstallationFile> CREATOR =
diff --git a/core/java/android/content/pm/InstantAppRequest.java b/core/java/android/content/pm/InstantAppRequest.java
index 84f5021..f692db1 100644
--- a/core/java/android/content/pm/InstantAppRequest.java
+++ b/core/java/android/content/pm/InstantAppRequest.java
@@ -35,8 +35,6 @@
     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 */
@@ -59,15 +57,13 @@
     public final String token;
 
     public InstantAppRequest(AuxiliaryResolveInfo responseObj, Intent origIntent,
-            String resolvedType, String callingPackage, @Nullable String callingFeatureId,
-            boolean isRequesterInstantApp, int userId, Bundle verificationBundle,
-            boolean resolveForStart, @Nullable int[] hostDigestPrefixSecure,
-            @NonNull String token) {
+            String resolvedType, String callingPackage, 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 271d5e4..70603b4 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -16,6 +16,7 @@
 
 package android.content.pm;
 
+import static android.Manifest.permission;
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -601,11 +602,15 @@
     }
 
     /**
-     * Show an error log on logcat, when the calling user is a managed profile, and the target
-     * user is different from the calling user, in order to help developers to detect it.
+     * Show an error log on logcat, when the calling user is a managed profile, the target
+     * user is different from the calling user, and it is not called from a package that has the
+     * {@link permission.INTERACT_ACROSS_USERS_FULL} permission, in order to help
+     * developers to detect it.
      */
     private void logErrorForInvalidProfileAccess(@NonNull UserHandle target) {
-        if (UserHandle.myUserId() != target.getIdentifier() && mUserManager.isManagedProfile()) {
+        if (UserHandle.myUserId() != target.getIdentifier() && mUserManager.isManagedProfile()
+                    && mContext.checkSelfPermission(permission.INTERACT_ACROSS_USERS_FULL)
+                            != PackageManager.PERMISSION_GRANTED) {
             Log.w(TAG, "Accessing other profiles/users from managed profile is no longer allowed.");
         }
     }
@@ -715,7 +720,7 @@
         }
         try {
             mService.startActivityAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(), mContext.getFeatureId(),
+                    mContext.getPackageName(),
                     component, sourceBounds, opts, user);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
@@ -733,8 +738,8 @@
             @Nullable Rect sourceBounds, @Nullable Bundle opts) {
         try {
             mService.startSessionDetailsActivityAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(), mContext.getFeatureId(), sessionInfo, sourceBounds,
-                    opts, sessionInfo.getUser());
+                    mContext.getPackageName(), sessionInfo, sourceBounds, opts,
+                    sessionInfo.getUser());
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
@@ -754,7 +759,7 @@
         logErrorForInvalidProfileAccess(user);
         try {
             mService.showAppDetailsAsUser(mContext.getIApplicationThread(),
-                    mContext.getPackageName(), mContext.getFeatureId(),
+                    mContext.getPackageName(),
                     component, sourceBounds, opts, user);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
@@ -1346,9 +1351,9 @@
             @Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
             int userId) {
         try {
-            final boolean success = mService.startShortcut(mContext.getPackageName(), packageName,
-                    null /* default featureId */, shortcutId, sourceBounds, startActivityOptions,
-                    userId);
+            final boolean success =
+                    mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
+                    sourceBounds, startActivityOptions, userId);
             if (!success) {
                 throw new ActivityNotFoundException("Shortcut could not be started");
             }
diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java
index fbe5a48..da17ff3 100644
--- a/core/java/android/content/pm/parsing/AndroidPackage.java
+++ b/core/java/android/content/pm/parsing/AndroidPackage.java
@@ -286,6 +286,8 @@
 
     List<String> getQueriesPackages();
 
+    Set<String> getQueriesProviders();
+
     String getRealPackage();
 
     // TODO(b/135203078): Rename to getRequiredFeatures? Somewhat ambiguous whether "Req" is
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index 5c8c9a4..548d82a 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -96,6 +96,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.StringTokenizer;
 
 /** @hide */
 public class ApkParseUtils {
@@ -1817,6 +1818,25 @@
                     );
                 }
                 parsingPackage.addQueriesPackage(packageName.intern());
+            } else if (parser.getName().equals("provider")) {
+                final TypedArray sa = res.obtainAttributes(parser,
+                        R.styleable.AndroidManifestQueriesProvider);
+                try {
+                    final String authorities =
+                            sa.getString(R.styleable.AndroidManifestQueriesProvider_authorities);
+                    if (TextUtils.isEmpty(authorities)) {
+                        return parseInput.error(
+                                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                                "Authority missing from provider tag."
+                        );
+                    }
+                    StringTokenizer authoritiesTokenizer = new StringTokenizer(authorities, ";");
+                    while (authoritiesTokenizer.hasMoreElements()) {
+                        parsingPackage.addQueriesProvider(authoritiesTokenizer.nextToken());
+                    }
+                } finally {
+                    sa.recycle();
+                }
             }
         }
         return parseInput.success(parsingPackage);
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
index fe8307c..0df9500 100644
--- a/core/java/android/content/pm/parsing/PackageImpl.java
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -216,6 +216,9 @@
     private ArrayList<String> queriesPackages;
 
     @Nullable
+    private ArraySet<String> queriesProviders;
+
+    @Nullable
     private ArrayMap<String, ComponentParseUtils.ParsedProcess> processes;
 
     private String[] splitClassLoaderNames;
@@ -957,6 +960,12 @@
     }
 
     @Override
+    public ParsingPackage addQueriesProvider(String authority) {
+        this.queriesProviders = ArrayUtils.add(this.queriesProviders, authority);
+        return this;
+    }
+
+    @Override
     public PackageImpl setProcesses(ArrayMap<String, ComponentParseUtils.ParsedProcess> processes) {
         this.processes = processes;
         return this;
@@ -2975,6 +2984,11 @@
         return queriesPackages;
     }
 
+    @Override
+    public Set<String> getQueriesProviders() {
+        return queriesProviders;
+    }
+
     private static void internStringArrayList(List<String> list) {
         if (list != null) {
             final int N = list.size();
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 9ddcc09..a2fe064 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -100,6 +100,8 @@
 
     ParsingPackage addQueriesPackage(String packageName);
 
+    ParsingPackage addQueriesProvider(String authority);
+
     ParsingPackage setProcesses(ArrayMap<String, ComponentParseUtils.ParsedProcess> processes);
 
     ParsingPackage asSplit(
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 8c358cc..6a9e0aa 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -45,6 +45,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.app.UiModeManager;
 import android.app.WindowConfiguration;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.LocaleProto;
@@ -1975,6 +1976,15 @@
         readFromParcel(source);
     }
 
+
+    /**
+     * Retuns whether the configuration is in night mode
+     * @return true if night mode is active and false otherwise
+     */
+    public boolean isNightModeActive() {
+        return (uiMode & UI_MODE_NIGHT_MASK) == UI_MODE_NIGHT_YES;
+    }
+
     public int compareTo(Configuration that) {
         int n;
         float a = this.fontScale;
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 5a13651..add67aa 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -132,6 +132,14 @@
     int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
 
     /**
+     * A security vulnerability has been discovered and the sensor is unavailable until a
+     * security update has addressed this issue. This error can be received if for example,
+     * authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
+     * sensor's strength can currently only meet {@link Authenticators#BIOMETRIC_WEAK}.
+     */
+    int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
+
+    /**
      * This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
      * because the authentication attempt was unsuccessful.
      * @hide
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index f6b64de..eafcf52 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -17,6 +17,7 @@
 package android.hardware.biometrics;
 
 import android.app.KeyguardManager;
+import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.hardware.face.FaceManager;
 
 /**
@@ -143,6 +144,15 @@
     int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
 
     /**
+     * A security vulnerability has been discovered and the sensor is unavailable until a
+     * security update has addressed this issue. This error can be received if for example,
+     * authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
+     * sensor's strength can currently only meet {@link Authenticators#BIOMETRIC_WEAK}.
+     * @hide
+     */
+    int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
+
+    /**
      * @hide
      */
     int FACE_ERROR_VENDOR_BASE = 1000;
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index 5fe5dc6..46e8cc0 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -18,6 +18,7 @@
 
 import android.app.KeyguardManager;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.hardware.biometrics.BiometricManager.Authenticators;
 import android.hardware.fingerprint.FingerprintManager;
 
 /**
@@ -130,6 +131,15 @@
     int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
 
     /**
+     * A security vulnerability has been discovered and the sensor is unavailable until a
+     * security update has addressed this issue. This error can be received if for example,
+     * authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
+     * sensor's strength can currently only meet {@link Authenticators#BIOMETRIC_WEAK}.
+     * @hide
+     */
+    public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
+
+    /**
      * @hide
      */
     @UnsupportedAppUsage
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 125b676..7d66cae 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -61,10 +61,20 @@
     public static final int BIOMETRIC_ERROR_NO_HARDWARE =
             BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
 
+    /**
+     * A security vulnerability has been discovered and the sensor is unavailable until a
+     * security update has addressed this issue. This error can be received if for example,
+     * authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
+     * sensor's strength can currently only meet {@link Authenticators#BIOMETRIC_WEAK}.
+     */
+    public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED =
+            BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
+
     @IntDef({BIOMETRIC_SUCCESS,
             BIOMETRIC_ERROR_HW_UNAVAILABLE,
             BIOMETRIC_ERROR_NONE_ENROLLED,
-            BIOMETRIC_ERROR_NO_HARDWARE})
+            BIOMETRIC_ERROR_NO_HARDWARE,
+            BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED})
     @interface BiometricError {}
 
     /**
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 987a53e..25fb3e0 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -29,6 +29,8 @@
  * @throws IllegalStateException the session is not an Incremental installation session.
  */
 
+import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -85,7 +87,7 @@
         try {
             result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams);
             for (InstallationFile file : addedFiles) {
-                if (file.getFileType() == InstallationFile.FILE_TYPE_APK) {
+                if (file.getLocation() == LOCATION_DATA_APP) {
                     try {
                         result.addApkFile(file);
                     } catch (IOException e) {
@@ -95,7 +97,7 @@
                                 e);
                     }
                 } else {
-                    throw new IOException("Unknown file type: " + file.getFileType());
+                    throw new IOException("Unknown file location: " + file.getLocation());
                 }
             }
 
@@ -147,8 +149,8 @@
         String apkName = apk.getName();
         File targetFile = Paths.get(stageDirPath, apkName).toFile();
         if (!targetFile.exists()) {
-            mDefaultStorage.makeFile(apkName, apk.getSize(), null,
-                    apk.getMetadata(), 0, null, null, null);
+            mDefaultStorage.makeFile(apkName, apk.getLengthBytes(), null, apk.getMetadata(),
+                    apk.getSignature());
         }
     }
 
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index ba38268..d2d8f85 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -299,7 +299,16 @@
         return nativeIsIncrementalPath(path);
     }
 
+    /**
+     * Returns raw signature for file if it's on Incremental File System.
+     * Unsafe, use only if you are sure what you are doing.
+     */
+    public static @Nullable byte[] unsafeGetFileSignature(@NonNull String path) {
+        return nativeUnsafeGetFileSignature(path);
+    }
+
     /* Native methods */
     private static native boolean nativeIsEnabled();
     private static native boolean nativeIsIncrementalPath(@NonNull String path);
+    private static native byte[] nativeUnsafeGetFileSignature(@NonNull String path);
 }
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index 5df44ff..f4e1f96 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -20,6 +20,8 @@
 import android.annotation.Nullable;
 import android.os.RemoteException;
 
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.nio.ByteBuffer;
@@ -169,10 +171,11 @@
      * @param path             Relative path of the new file.
      * @param size             Size of the new file in bytes.
      * @param metadata         Metadata bytes.
+     * @param v4signatureBytes Serialized V4SignatureProto.
      */
     public void makeFile(@NonNull String path, long size, @Nullable UUID id,
-            @Nullable byte[] metadata, int hashAlgorithm, @Nullable byte[] rootHash,
-            @Nullable byte[] additionalData, @Nullable byte[] signature) throws IOException {
+            @Nullable byte[] metadata, @Nullable byte[] v4signatureBytes)
+            throws IOException {
         try {
             if (id == null && metadata == null) {
                 throw new IOException("File ID and metadata cannot both be null");
@@ -181,13 +184,7 @@
             params.size = size;
             params.metadata = (metadata == null ? new byte[0] : metadata);
             params.fileId = idToBytes(id);
-            if (hashAlgorithm != 0 || signature != null) {
-                params.signature = new IncrementalSignature();
-                params.signature.hashAlgorithm = hashAlgorithm;
-                params.signature.rootHash = rootHash;
-                params.signature.additionalData = additionalData;
-                params.signature.signature = signature;
-            }
+            params.signature = parseV4Signature(v4signatureBytes);
             int res = mService.makeFile(mId, path, params);
             if (res != 0) {
                 throw new IOException("makeFile() failed with errno " + -res);
@@ -197,6 +194,7 @@
         }
     }
 
+
     /**
      * Creates a file in Incremental storage. The content of the file is mapped from a range inside
      * a source file in the same storage.
@@ -349,6 +347,37 @@
         }
     }
 
+    /**
+     * Returns the metadata object of an IncFs File.
+     *
+     * @param id The file id.
+     * @return Byte array that contains metadata bytes.
+     */
+    @Nullable
+    public byte[] getFileMetadata(@NonNull UUID id) {
+        try {
+            final byte[] rawId = idToBytes(id);
+            return mService.getMetadataById(mId, rawId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return null;
+        }
+    }
+
+    /**
+     * Informs the data loader service associated with the current storage to start data loader
+     *
+     * @return True if data loader is successfully started.
+     */
+    public boolean startLoading() {
+        try {
+            return mService.startLoading(mId);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return false;
+        }
+    }
+
     private static final int UUID_BYTE_SIZE = 16;
 
     /**
@@ -386,35 +415,44 @@
         return new UUID(msb, lsb);
     }
 
-    /**
-     * Returns the metadata object of an IncFs File.
-     *
-     * @param id The file id.
-     * @return Byte array that contains metadata bytes.
-     */
-    @Nullable
-    public byte[] getFileMetadata(@NonNull UUID id) {
-        try {
-            final byte[] rawId = idToBytes(id);
-            return mService.getMetadataById(mId, rawId);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-            return null;
-        }
-    }
+    private static final int INCFS_HASH_SHA256 = 1;
+    private static final int INCFS_MAX_HASH_SIZE = 32; // SHA256
+    private static final int INCFS_MAX_ADD_DATA_SIZE = 128;
 
     /**
-     * Informs the data loader service associated with the current storage to start data loader
-     *
-     * @return True if data loader is successfully started.
+     * Deserialize and validate v4 signature bytes.
      */
-    public boolean startLoading() {
-        try {
-            return mService.startLoading(mId);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-            return false;
+    private static IncrementalSignature parseV4Signature(@Nullable byte[] v4signatureBytes)
+            throws IOException {
+        if (v4signatureBytes == null) {
+            return null;
         }
+
+        final V4Signature signature;
+        try (DataInputStream input = new DataInputStream(
+                new ByteArrayInputStream(v4signatureBytes))) {
+            signature = V4Signature.readFrom(input);
+        }
+
+        final byte[] rootHash = signature.verityRootHash;
+        final byte[] additionalData = signature.v3Digest;
+        final byte[] pkcs7Signature = signature.pkcs7SignatureBlock;
+
+        if (rootHash.length != INCFS_MAX_HASH_SIZE) {
+            throw new IOException("rootHash has to be " + INCFS_MAX_HASH_SIZE + " bytes");
+        }
+        if (additionalData.length > INCFS_MAX_ADD_DATA_SIZE) {
+            throw new IOException(
+                    "additionalData has to be at most " + INCFS_MAX_ADD_DATA_SIZE + " bytes");
+        }
+
+        IncrementalSignature result = new IncrementalSignature();
+        result.hashAlgorithm = INCFS_HASH_SHA256;
+        result.rootHash = rootHash;
+        result.additionalData = additionalData;
+        result.signature = pkcs7Signature;
+
+        return result;
     }
 
     /**
diff --git a/core/java/android/os/incremental/V4Signature.java b/core/java/android/os/incremental/V4Signature.java
new file mode 100644
index 0000000..5fadee4
--- /dev/null
+++ b/core/java/android/os/incremental/V4Signature.java
@@ -0,0 +1,62 @@
+/*
+ * 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.os.incremental;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+/**
+ * V4 signature fields.
+ * Keep in sync with APKSig master copy.
+ * @hide
+ */
+public class V4Signature {
+    public final byte[] verityRootHash;
+    public final byte[] v3Digest;
+    public final byte[] pkcs7SignatureBlock;
+
+    V4Signature(byte[] verityRootHash, byte[] v3Digest, byte[] pkcs7SignatureBlock) {
+        this.verityRootHash = verityRootHash;
+        this.v3Digest = v3Digest;
+        this.pkcs7SignatureBlock = pkcs7SignatureBlock;
+    }
+
+    static byte[] readBytes(DataInputStream stream) throws IOException {
+        byte[] result = new byte[stream.readInt()];
+        stream.read(result);
+        return result;
+    }
+
+    static V4Signature readFrom(DataInputStream stream) throws IOException {
+        byte[] verityRootHash = readBytes(stream);
+        byte[] v3Digest = readBytes(stream);
+        byte[] pkcs7SignatureBlock = readBytes(stream);
+        return new V4Signature(verityRootHash, v3Digest, pkcs7SignatureBlock);
+    }
+
+    static void writeBytes(DataOutputStream stream, byte[] bytes) throws IOException {
+        stream.writeInt(bytes.length);
+        stream.write(bytes);
+    }
+
+    void writeTo(DataOutputStream stream) throws IOException {
+        writeBytes(stream, this.verityRootHash);
+        writeBytes(stream, this.v3Digest);
+        writeBytes(stream, this.pkcs7SignatureBlock);
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b9207e5..ea0afa9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10207,7 +10207,9 @@
          *
          * Type: int (0 for false, 1 for true)
          * @hide
+         * @deprecated Use {@link WifiManager#isAutoWakeupEnabled()} instead.
          */
+        @Deprecated
         @SystemApi
         public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
 
@@ -10243,7 +10245,6 @@
          * enabled state.
          * @hide
          */
-        @SystemApi
         public static final String NETWORK_RECOMMENDATIONS_ENABLED =
                 "network_recommendations_enabled";
 
diff --git a/core/java/android/service/dataloader/DataLoaderService.java b/core/java/android/service/dataloader/DataLoaderService.java
index c215778..b9700b2 100644
--- a/core/java/android/service/dataloader/DataLoaderService.java
+++ b/core/java/android/service/dataloader/DataLoaderService.java
@@ -90,7 +90,7 @@
      * @hide
      */
     @SystemApi
-    public @Nullable DataLoader onCreateDataLoader() {
+    public @Nullable DataLoader onCreateDataLoader(@NonNull DataLoaderParams dataLoaderParams) {
         return null;
     }
 
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index e053ed5..0ff2e03 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -214,6 +214,32 @@
     public static final int REASON_TIMEOUT = 19;
 
     /**
+     * @hide
+     */
+    @IntDef(prefix = "REASON_", value = {
+            REASON_CLICK,
+            REASON_CANCEL,
+            REASON_CANCEL_ALL,
+            REASON_ERROR,
+            REASON_PACKAGE_CHANGED,
+            REASON_USER_STOPPED,
+            REASON_PACKAGE_BANNED,
+            REASON_APP_CANCEL,
+            REASON_APP_CANCEL_ALL,
+            REASON_LISTENER_CANCEL,
+            REASON_LISTENER_CANCEL_ALL,
+            REASON_GROUP_SUMMARY_CANCELED,
+            REASON_GROUP_OPTIMIZATION,
+            REASON_PACKAGE_SUSPENDED,
+            REASON_PROFILE_TURNED_OFF,
+            REASON_UNAUTOBUNDLED,
+            REASON_CHANNEL_BANNED,
+            REASON_SNOOZED,
+            REASON_TIMEOUT
+    })
+    public @interface NotificationCancelReason{};
+
+    /**
      * The full trim of the StatusBarNotification including all its features.
      *
      * @hide
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 8e6f77b..36f2c62 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()), mContext.getFeatureId());
+                    intent.resolveType(mContext.getContentResolver()));
             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()), mContext.getFeatureId());
+                    intent.resolveType(mContext.getContentResolver()));
             Instrumentation.checkStartActivityResult(res, intent);
         } catch (RemoteException e) {
         }
diff --git a/core/java/android/view/CutoutSpecification.java b/core/java/android/view/CutoutSpecification.java
new file mode 100644
index 0000000..d21a952
--- /dev/null
+++ b/core/java/android/view/CutoutSpecification.java
@@ -0,0 +1,486 @@
+/*
+ * 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.view;
+
+import static android.view.Gravity.BOTTOM;
+import static android.view.Gravity.LEFT;
+import static android.view.Gravity.RIGHT;
+import static android.view.Gravity.TOP;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Insets;
+import android.graphics.Matrix;
+import android.graphics.Path;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.PathParser;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Locale;
+import java.util.Objects;
+
+/**
+ * In order to accept the cutout specification for all of edges in devices, the specification
+ * parsing method is extracted from
+ * {@link android.view.DisplayCutout#fromResourcesRectApproximation(Resources, int, int)} to be
+ * the specified class for parsing the specification.
+ * BNF definition:
+ * <ul>
+ *      <li>Cutouts Specification = ([Cutout Delimiter],Cutout Specification) {...}, [Dp] ; </li>
+ *      <li>Cutout Specification  = [Vertical Position], (SVG Path Element), [Horizontal Position]
+ *                                  [Bind Cutout] ;</li>
+ *      <li>Vertical Position     = "@bottom" | "@center_vertical" ;</li>
+ *      <li>Horizontal Position   = "@left" | "@right" ;</li>
+ *      <li>Bind Cutout           = "@bind_left_cutout" | "@bind_right_cutout" ;</li>
+ *      <li>Cutout Delimiter      = "@cutout" ;</li>
+ *      <li>Dp                    = "@dp"</li>
+ * </ul>
+ *
+ * <ul>
+ *     <li>Vertical position is top by default if there is neither "@bottom" nor "@center_vertical"
+ *     </li>
+ *     <li>Horizontal position is center horizontal by default if there is neither "@left" nor
+ *     "@right".</li>
+ *     <li>@bottom make the cutout piece bind to bottom edge.</li>
+ *     <li>both of @bind_left_cutout and @bind_right_cutout are use to claim the cutout belong to
+ *     left or right edge cutout.</li>
+ * </ul>
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = PACKAGE)
+public class CutoutSpecification {
+    private static final String TAG = "CutoutSpecification";
+    private static final boolean DEBUG = false;
+
+    private static final int MINIMAL_ACCEPTABLE_PATH_LENGTH = "H1V1Z".length();
+
+    private static final char MARKER_START_CHAR = '@';
+    private static final String DP_MARKER = MARKER_START_CHAR + "dp";
+
+    private static final String BOTTOM_MARKER = MARKER_START_CHAR + "bottom";
+    private static final String RIGHT_MARKER = MARKER_START_CHAR + "right";
+    private static final String LEFT_MARKER = MARKER_START_CHAR + "left";
+    private static final String CUTOUT_MARKER = MARKER_START_CHAR + "cutout";
+    private static final String CENTER_VERTICAL_MARKER = MARKER_START_CHAR + "center_vertical";
+
+    /* By default, it's top bound cutout. That's why TOP_BOUND_CUTOUT_MARKER is not defined */
+    private static final String BIND_RIGHT_CUTOUT_MARKER = MARKER_START_CHAR + "bind_right_cutout";
+    private static final String BIND_LEFT_CUTOUT_MARKER = MARKER_START_CHAR + "bind_left_cutout";
+
+    private final Path mPath;
+    private final Rect mLeftBound;
+    private final Rect mTopBound;
+    private final Rect mRightBound;
+    private final Rect mBottomBound;
+    private final Insets mInsets;
+
+    private CutoutSpecification(@NonNull Parser parser) {
+        mPath = parser.mPath;
+        mLeftBound = parser.mLeftBound;
+        mTopBound = parser.mTopBound;
+        mRightBound = parser.mRightBound;
+        mBottomBound = parser.mBottomBound;
+        mInsets = parser.mInsets;
+
+        if (DEBUG) {
+            Log.d(TAG, String.format(Locale.ENGLISH,
+                    "left cutout = %s, top cutout = %s, right cutout = %s, bottom cutout = %s",
+                    mLeftBound != null ? mLeftBound.toString() : "",
+                    mTopBound != null ? mTopBound.toString() : "",
+                    mRightBound != null ? mRightBound.toString() : "",
+                    mBottomBound != null ? mBottomBound.toString() : ""));
+        }
+    }
+
+    @VisibleForTesting(visibility = PACKAGE)
+    @Nullable
+    public Path getPath() {
+        return mPath;
+    }
+
+    @VisibleForTesting(visibility = PACKAGE)
+    @Nullable
+    public Rect getLeftBound() {
+        return mLeftBound;
+    }
+
+    @VisibleForTesting(visibility = PACKAGE)
+    @Nullable
+    public Rect getTopBound() {
+        return mTopBound;
+    }
+
+    @VisibleForTesting(visibility = PACKAGE)
+    @Nullable
+    public Rect getRightBound() {
+        return mRightBound;
+    }
+
+    @VisibleForTesting(visibility = PACKAGE)
+    @Nullable
+    public Rect getBottomBound() {
+        return mBottomBound;
+    }
+
+    /**
+     * To count the safe inset according to the cutout bounds and waterfall inset.
+     *
+     * @return the safe inset.
+     */
+    @VisibleForTesting(visibility = PACKAGE)
+    @NonNull
+    public Rect getSafeInset() {
+        return mInsets.toRect();
+    }
+
+    private static int decideWhichEdge(boolean isTopEdgeShortEdge,
+            boolean isShortEdge, boolean isStart) {
+        return (isTopEdgeShortEdge)
+                ? ((isShortEdge) ? (isStart ? TOP : BOTTOM) : (isStart ? LEFT : RIGHT))
+                : ((isShortEdge) ? (isStart ? LEFT : RIGHT) : (isStart ? TOP : BOTTOM));
+    }
+
+    /**
+     * The CutoutSpecification Parser.
+     */
+    @VisibleForTesting(visibility = PACKAGE)
+    public static class Parser {
+        private final boolean mIsShortEdgeOnTop;
+        private final float mDensity;
+        private final int mDisplayWidth;
+        private final int mDisplayHeight;
+        private final Matrix mMatrix;
+        private Insets mInsets;
+        private int mSafeInsetLeft;
+        private int mSafeInsetTop;
+        private int mSafeInsetRight;
+        private int mSafeInsetBottom;
+
+        private final Rect mTmpRect = new Rect();
+        private final RectF mTmpRectF = new RectF();
+
+        private boolean mInDp;
+
+        private Path mPath;
+        private Rect mLeftBound;
+        private Rect mTopBound;
+        private Rect mRightBound;
+        private Rect mBottomBound;
+
+        private boolean mPositionFromLeft = false;
+        private boolean mPositionFromRight = false;
+        private boolean mPositionFromBottom = false;
+        private boolean mPositionFromCenterVertical = false;
+
+        private boolean mBindLeftCutout = false;
+        private boolean mBindRightCutout = false;
+        private boolean mBindBottomCutout = false;
+
+        private boolean mIsTouchShortEdgeStart;
+        private boolean mIsTouchShortEdgeEnd;
+        private boolean mIsCloserToStartSide;
+
+        /**
+         * The constructor of the CutoutSpecification parser to parse the specification of cutout.
+         * @param density the display density.
+         * @param displayWidth the display width.
+         * @param displayHeight the display height.
+         */
+        @VisibleForTesting(visibility = PACKAGE)
+        public Parser(float density, int displayWidth, int displayHeight) {
+            mDensity = density;
+            mDisplayWidth = displayWidth;
+            mDisplayHeight = displayHeight;
+            mMatrix = new Matrix();
+            mIsShortEdgeOnTop = mDisplayWidth < mDisplayHeight;
+        }
+
+        private void computeBoundsRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
+            mTmpRectF.setEmpty();
+            p.computeBounds(mTmpRectF, false /* unused */);
+            mTmpRectF.round(inoutRect);
+            inoutRegion.op(inoutRect, Region.Op.UNION);
+        }
+
+        private void resetStatus(StringBuilder sb) {
+            sb.setLength(0);
+            mPositionFromBottom = false;
+            mPositionFromLeft = false;
+            mPositionFromRight = false;
+            mPositionFromCenterVertical = false;
+
+            mBindLeftCutout = false;
+            mBindRightCutout = false;
+            mBindBottomCutout = false;
+        }
+
+        private void translateMatrix() {
+            final float offsetX;
+            if (mPositionFromRight) {
+                offsetX = mDisplayWidth;
+            } else if (mPositionFromLeft) {
+                offsetX = 0;
+            } else {
+                offsetX = mDisplayWidth / 2f;
+            }
+
+            final float offsetY;
+            if (mPositionFromBottom) {
+                offsetY = mDisplayHeight;
+            } else if (mPositionFromCenterVertical) {
+                offsetY = mDisplayHeight / 2f;
+            } else {
+                offsetY = 0;
+            }
+
+            mMatrix.reset();
+            if (mInDp) {
+                mMatrix.postScale(mDensity, mDensity);
+            }
+            mMatrix.postTranslate(offsetX, offsetY);
+        }
+
+        private int computeSafeInsets(int gravity, Rect rect) {
+            if (gravity == LEFT && rect.right > 0 && rect.right < mDisplayWidth) {
+                return rect.right;
+            } else if (gravity == TOP && rect.bottom > 0 && rect.bottom < mDisplayHeight) {
+                return rect.bottom;
+            } else if (gravity == RIGHT && rect.left > 0 && rect.left < mDisplayWidth) {
+                return mDisplayWidth - rect.left;
+            } else if (gravity == BOTTOM && rect.top > 0 && rect.top < mDisplayHeight) {
+                return mDisplayHeight - rect.top;
+            }
+            return 0;
+        }
+
+        private void setSafeInset(int gravity, int inset) {
+            if (gravity == LEFT) {
+                mSafeInsetLeft = inset;
+            } else if (gravity == TOP) {
+                mSafeInsetTop = inset;
+            } else if (gravity == RIGHT) {
+                mSafeInsetRight = inset;
+            } else if (gravity == BOTTOM) {
+                mSafeInsetBottom = inset;
+            }
+        }
+
+        private int getSafeInset(int gravity) {
+            if (gravity == LEFT) {
+                return mSafeInsetLeft;
+            } else if (gravity == TOP) {
+                return mSafeInsetTop;
+            } else if (gravity == RIGHT) {
+                return mSafeInsetRight;
+            } else if (gravity == BOTTOM) {
+                return mSafeInsetBottom;
+            }
+            return 0;
+        }
+
+        @NonNull
+        private Rect onSetEdgeCutout(boolean isStart, boolean isShortEdge, @NonNull Rect rect) {
+            final int gravity;
+            if (isShortEdge) {
+                gravity = decideWhichEdge(mIsShortEdgeOnTop, true, isStart);
+            } else {
+                if (mIsTouchShortEdgeStart && mIsTouchShortEdgeEnd) {
+                    gravity = decideWhichEdge(mIsShortEdgeOnTop, false, isStart);
+                } else if (mIsTouchShortEdgeStart || mIsTouchShortEdgeEnd) {
+                    gravity = decideWhichEdge(mIsShortEdgeOnTop, true,
+                            mIsCloserToStartSide);
+                } else {
+                    gravity = decideWhichEdge(mIsShortEdgeOnTop, isShortEdge, isStart);
+                }
+            }
+
+            int oldSafeInset = getSafeInset(gravity);
+            int newSafeInset = computeSafeInsets(gravity, rect);
+            if (oldSafeInset < newSafeInset) {
+                setSafeInset(gravity, newSafeInset);
+            }
+
+            return new Rect(rect);
+        }
+
+        private void setEdgeCutout(@NonNull Path newPath) {
+            if (mBindRightCutout && mRightBound == null) {
+                mRightBound = onSetEdgeCutout(false, !mIsShortEdgeOnTop, mTmpRect);
+            } else if (mBindLeftCutout && mLeftBound == null) {
+                mLeftBound = onSetEdgeCutout(true, !mIsShortEdgeOnTop, mTmpRect);
+            } else if (mBindBottomCutout && mBottomBound == null) {
+                mBottomBound = onSetEdgeCutout(false, mIsShortEdgeOnTop, mTmpRect);
+            } else if (!(mBindBottomCutout || mBindLeftCutout || mBindRightCutout)
+                    && mTopBound == null) {
+                mTopBound = onSetEdgeCutout(true, mIsShortEdgeOnTop, mTmpRect);
+            } else {
+                return;
+            }
+
+            if (mPath != null) {
+                mPath.addPath(newPath);
+            } else {
+                mPath = newPath;
+            }
+        }
+
+        private void parseSvgPathSpec(Region region, String spec) {
+            if (TextUtils.length(spec) < MINIMAL_ACCEPTABLE_PATH_LENGTH) {
+                Log.e(TAG, "According to SVG definition, it shouldn't happen");
+                return;
+            }
+            spec.trim();
+            translateMatrix();
+
+            final Path newPath = PathParser.createPathFromPathData(spec);
+            newPath.transform(mMatrix);
+            computeBoundsRectAndAddToRegion(newPath, region, mTmpRect);
+
+            if (DEBUG) {
+                Log.d(TAG, String.format(Locale.ENGLISH,
+                        "hasLeft = %b, hasRight = %b, hasBottom = %b, hasCenterVertical = %b",
+                        mPositionFromLeft, mPositionFromRight, mPositionFromBottom,
+                        mPositionFromCenterVertical));
+                Log.d(TAG, "region = " + region);
+                Log.d(TAG, "spec = \"" + spec + "\" rect = " + mTmpRect + " newPath = " + newPath);
+            }
+
+            if (mTmpRect.isEmpty()) {
+                return;
+            }
+
+            if (mIsShortEdgeOnTop) {
+                mIsTouchShortEdgeStart = mTmpRect.top <= 0;
+                mIsTouchShortEdgeEnd = mTmpRect.bottom >= mDisplayHeight;
+                mIsCloserToStartSide = mTmpRect.centerY() < mDisplayHeight / 2;
+            } else {
+                mIsTouchShortEdgeStart = mTmpRect.left <= 0;
+                mIsTouchShortEdgeEnd = mTmpRect.right >= mDisplayWidth;
+                mIsCloserToStartSide = mTmpRect.centerX() < mDisplayWidth / 2;
+            }
+
+            setEdgeCutout(newPath);
+        }
+
+        private void parseSpecWithoutDp(@NonNull String specWithoutDp) {
+            Region region = Region.obtain();
+            StringBuilder sb = null;
+            int currentIndex = 0;
+            int lastIndex = 0;
+            while ((currentIndex = specWithoutDp.indexOf(MARKER_START_CHAR, lastIndex)) != -1) {
+                if (sb == null) {
+                    sb = new StringBuilder(specWithoutDp.length());
+                }
+                sb.append(specWithoutDp, lastIndex, currentIndex);
+
+                if (specWithoutDp.startsWith(LEFT_MARKER, currentIndex)) {
+                    if (!mPositionFromRight) {
+                        mPositionFromLeft = true;
+                    }
+                    currentIndex += LEFT_MARKER.length();
+                } else if (specWithoutDp.startsWith(RIGHT_MARKER, currentIndex)) {
+                    if (!mPositionFromLeft) {
+                        mPositionFromRight = true;
+                    }
+                    currentIndex += RIGHT_MARKER.length();
+                } else if (specWithoutDp.startsWith(BOTTOM_MARKER, currentIndex)) {
+                    if (!mPositionFromCenterVertical) {
+                        parseSvgPathSpec(region, sb.toString());
+                    }
+                    currentIndex += BOTTOM_MARKER.length();
+
+                    /* prepare to parse the rest path */
+                    resetStatus(sb);
+                    mBindBottomCutout = true;
+                    mPositionFromBottom = true;
+                } else if (specWithoutDp.startsWith(CENTER_VERTICAL_MARKER, currentIndex)) {
+                    if (!mPositionFromBottom) {
+                        parseSvgPathSpec(region, sb.toString());
+                    }
+                    currentIndex += CENTER_VERTICAL_MARKER.length();
+
+                    /* prepare to parse the rest path */
+                    resetStatus(sb);
+                    mPositionFromCenterVertical = true;
+                } else if (specWithoutDp.startsWith(CUTOUT_MARKER, currentIndex)) {
+                    parseSvgPathSpec(region, sb.toString());
+                    currentIndex += CUTOUT_MARKER.length();
+
+                    /* prepare to parse the rest path */
+                    resetStatus(sb);
+                } else if (specWithoutDp.startsWith(BIND_LEFT_CUTOUT_MARKER, currentIndex)) {
+                    if (!mBindBottomCutout && !mBindRightCutout) {
+                        mBindLeftCutout = true;
+                    }
+                    currentIndex += BIND_LEFT_CUTOUT_MARKER.length();
+                } else if (specWithoutDp.startsWith(BIND_RIGHT_CUTOUT_MARKER, currentIndex)) {
+                    if (!mBindBottomCutout && !mBindLeftCutout) {
+                        mBindRightCutout = true;
+                    }
+                    currentIndex += BIND_RIGHT_CUTOUT_MARKER.length();
+                } else {
+                    currentIndex += 1;
+                }
+
+                lastIndex = currentIndex;
+            }
+
+            if (sb == null) {
+                parseSvgPathSpec(region, specWithoutDp);
+            } else {
+                sb.append(specWithoutDp, lastIndex, specWithoutDp.length());
+                parseSvgPathSpec(region, sb.toString());
+            }
+
+            region.recycle();
+        }
+
+        /**
+         * To parse specification string as the CutoutSpecification.
+         *
+         * @param originalSpec the specification string
+         * @return the CutoutSpecification instance
+         */
+        @VisibleForTesting(visibility = PACKAGE)
+        public CutoutSpecification parse(@NonNull String originalSpec) {
+            Objects.requireNonNull(originalSpec);
+
+            int dpIndex = originalSpec.lastIndexOf(DP_MARKER);
+            mInDp = (dpIndex != -1);
+            final String spec;
+            if (dpIndex != -1) {
+                spec = originalSpec.substring(0, dpIndex)
+                        + originalSpec.substring(dpIndex + DP_MARKER.length());
+            } else {
+                spec = originalSpec;
+            }
+
+            parseSpecWithoutDp(spec);
+
+            mInsets = Insets.of(mSafeInsetLeft, mSafeInsetTop, mSafeInsetRight, mSafeInsetBottom);
+            return new CutoutSpecification(this);
+        }
+    }
+}
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index d433591..31fc161 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -31,18 +31,12 @@
 import android.annotation.Nullable;
 import android.content.res.Resources;
 import android.graphics.Insets;
-import android.graphics.Matrix;
 import android.graphics.Path;
 import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.graphics.Region.Op;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
-import android.util.Log;
 import android.util.Pair;
-import android.util.PathParser;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
@@ -63,10 +57,6 @@
 public final class DisplayCutout {
 
     private static final String TAG = "DisplayCutout";
-    private static final String BOTTOM_MARKER = "@bottom";
-    private static final String DP_MARKER = "@dp";
-    private static final String RIGHT_MARKER = "@right";
-    private static final String LEFT_MARKER = "@left";
 
     /**
      * Category for overlays that allow emulating a display cutout on devices that don't have
@@ -703,77 +693,16 @@
             }
         }
 
-        Path p = null;
-        Rect boundTop = null;
-        Rect boundBottom = null;
-        Rect safeInset = new Rect();
-        String bottomSpec = null;
-        if (!TextUtils.isEmpty(spec)) {
-            spec = spec.trim();
-            final float offsetX;
-            if (spec.endsWith(RIGHT_MARKER)) {
-                offsetX = displayWidth;
-                spec = spec.substring(0, spec.length() - RIGHT_MARKER.length()).trim();
-            } else if (spec.endsWith(LEFT_MARKER)) {
-                offsetX = 0;
-                spec = spec.substring(0, spec.length() - LEFT_MARKER.length()).trim();
-            } else {
-                offsetX = displayWidth / 2f;
-            }
-            final boolean inDp = spec.endsWith(DP_MARKER);
-            if (inDp) {
-                spec = spec.substring(0, spec.length() - DP_MARKER.length());
-            }
+        spec = spec.trim();
 
-            if (spec.contains(BOTTOM_MARKER)) {
-                String[] splits = spec.split(BOTTOM_MARKER, 2);
-                spec = splits[0].trim();
-                bottomSpec = splits[1].trim();
-            }
+        CutoutSpecification cutoutSpec = new CutoutSpecification.Parser(density,
+                displayWidth, displayHeight).parse(spec);
+        Rect safeInset = cutoutSpec.getSafeInset();
+        final Rect boundLeft = cutoutSpec.getLeftBound();
+        final Rect boundTop = cutoutSpec.getTopBound();
+        final Rect boundRight = cutoutSpec.getRightBound();
+        final Rect boundBottom = cutoutSpec.getBottomBound();
 
-            final Matrix m = new Matrix();
-            final Region r = Region.obtain();
-            if (!spec.isEmpty()) {
-                try {
-                    p = PathParser.createPathFromPathData(spec);
-                } catch (Throwable e) {
-                    Log.wtf(TAG, "Could not inflate cutout: ", e);
-                }
-
-                if (p != null) {
-                    if (inDp) {
-                        m.postScale(density, density);
-                    }
-                    m.postTranslate(offsetX, 0);
-                    p.transform(m);
-
-                    boundTop = new Rect();
-                    toRectAndAddToRegion(p, r, boundTop);
-                    safeInset.top = boundTop.bottom;
-                }
-            }
-
-            if (bottomSpec != null) {
-                int bottomInset = 0;
-                Path bottomPath = null;
-                try {
-                    bottomPath = PathParser.createPathFromPathData(bottomSpec);
-                } catch (Throwable e) {
-                    Log.wtf(TAG, "Could not inflate bottom cutout: ", e);
-                }
-
-                if (bottomPath != null) {
-                    // Keep top transform
-                    m.postTranslate(0, displayHeight);
-                    bottomPath.transform(m);
-                    p.addPath(bottomPath);
-                    boundBottom = new Rect();
-                    toRectAndAddToRegion(bottomPath, r, boundBottom);
-                    bottomInset = displayHeight - boundBottom.top;
-                }
-                safeInset.bottom = bottomInset;
-            }
-        }
 
         if (!waterfallInsets.equals(Insets.NONE)) {
             safeInset.set(
@@ -784,9 +713,9 @@
         }
 
         final DisplayCutout cutout = new DisplayCutout(
-                safeInset, waterfallInsets, null /* boundLeft */, boundTop,
-                null /* boundRight */, boundBottom, false /* copyArguments */);
-        final Pair<Path, DisplayCutout> result = new Pair<>(p, cutout);
+                safeInset, waterfallInsets, boundLeft, boundTop,
+                boundRight, boundBottom, false /* copyArguments */);
+        final Pair<Path, DisplayCutout> result = new Pair<>(cutoutSpec.getPath(), cutout);
         synchronized (CACHE_LOCK) {
             sCachedSpec = spec;
             sCachedDisplayWidth = displayWidth;
@@ -798,14 +727,6 @@
         return result;
     }
 
-    private static void toRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
-        final RectF rectF = new RectF();
-        p.computeBounds(rectF, false /* unused */);
-        rectF.round(inoutRect);
-        inoutRegion.op(inoutRect, Op.UNION);
-    }
-
-
     private static Insets loadWaterfallInset(Resources res) {
         return Insets.of(
                 res.getDimensionPixelSize(R.dimen.waterfall_display_left_edge_size),
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index f9a023f..1730347 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -97,6 +97,8 @@
 
     IWindowSession openSession(in IWindowSessionCallback callback);
 
+    boolean useBLAST();
+
     @UnsupportedAppUsage
     void getInitialDisplaySize(int displayId, out Point size);
     @UnsupportedAppUsage
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index d4961ea..6caa4fe 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -198,7 +198,7 @@
         return "InsetsSource: {"
                 + "mType=" + InsetsState.typeToString(mType)
                 + ", mFrame=" + mFrame.toShortString()
-                + ", mVisible" + mVisible
+                + ", mVisible=" + mVisible
                 + "}";
     }
 
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 78a080d..4ac6a66 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -258,14 +258,14 @@
      */
     public void release() {
         synchronized (mLock) {
-            if (mNativeObject != 0) {
-                nativeRelease(mNativeObject);
-                setNativeObjectLocked(0);
-            }
             if (mHwuiContext != null) {
                 mHwuiContext.destroy();
                 mHwuiContext = null;
             }
+            if (mNativeObject != 0) {
+                nativeRelease(mNativeObject);
+                setNativeObjectLocked(0);
+            }
         }
     }
 
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index b1c354f..637a088 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -22,7 +22,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.CompatibilityInfo.Translator;
@@ -43,8 +42,8 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.SurfaceControl.Transaction;
-import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.SurfaceControlViewHost;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import com.android.internal.view.SurfaceCallbackHelper;
 
@@ -386,7 +385,7 @@
                  * This gets called on a RenderThread worker thread, so members accessed here must
                  * be protected by a lock.
                  */
-                final boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER;
+                final boolean useBLAST = WindowManagerGlobal.getInstance().useBLAST();
                 viewRoot.registerRtFrameCallback(frame -> {
                     try {
                         final SurfaceControl.Transaction t = useBLAST ?
@@ -1120,7 +1119,7 @@
 
     private void applySurfaceTransforms(SurfaceControl surface, SurfaceControl.Transaction t,
             Rect position, long frameNumber) {
-        if (frameNumber > 0 && !WindowManagerGlobal.USE_BLAST_ADAPTER) {
+        if (frameNumber > 0 && !WindowManagerGlobal.getInstance().useBLAST()) {
             final ViewRootImpl viewRoot = getViewRootImpl();
 
             t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(),
@@ -1138,7 +1137,7 @@
     }
 
     private void setParentSpaceRectangle(Rect position, long frameNumber) {
-        final boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER;
+        final boolean useBLAST = WindowManagerGlobal.getInstance().useBLAST();
         final ViewRootImpl viewRoot = getViewRootImpl();
         final SurfaceControl.Transaction t = useBLAST ? viewRoot.getBLASTSyncTransaction() :
             mRtTransaction;
@@ -1199,7 +1198,7 @@
 
         @Override
         public void positionLost(long frameNumber) {
-            boolean useBLAST = WindowManagerGlobal.USE_BLAST_ADAPTER;
+            boolean useBLAST = WindowManagerGlobal.getInstance().useBLAST();
             if (DEBUG) {
                 Log.d(TAG, String.format("%d windowPositionLost, frameNr = %d",
                         System.identityHashCode(this), frameNumber));
@@ -1538,7 +1537,7 @@
     @Override
     public void invalidate(boolean invalidateCache) {
         super.invalidate(invalidateCache);
-        if (!WindowManagerGlobal.USE_BLAST_ADAPTER) {
+        if (!WindowManagerGlobal.getInstance().useBLAST()) {
             return;
         }
         final ViewRootImpl viewRoot = getViewRootImpl();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 159b93e..ebfe66f7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -315,6 +315,8 @@
      */
     private boolean mForceNextConfigUpdate;
 
+    private final boolean mUseBLASTAdapter;
+
     /**
      * Signals that compatibility booleans have been initialized according to
      * target SDK versions.
@@ -734,6 +736,7 @@
 
         loadSystemProperties();
         mImeFocusController = new ImeFocusController(this);
+        mUseBLASTAdapter = WindowManagerGlobal.getInstance().useBLAST();
     }
 
     public static void addFirstDrawHandler(Runnable callback) {
@@ -861,7 +864,7 @@
                 if (mWindowAttributes.packageName == null) {
                     mWindowAttributes.packageName = mBasePackageName;
                 }
-                if (WindowManagerGlobal.USE_BLAST_ADAPTER) {
+                if (mUseBLASTAdapter) {
                     mWindowAttributes.privateFlags |=
                         WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
                 }
@@ -1341,7 +1344,7 @@
             }
             mWindowAttributes.privateFlags |= compatibleWindowFlag;
 
-            if (WindowManagerGlobal.USE_BLAST_ADAPTER) {
+            if (mUseBLASTAdapter) {
                 mWindowAttributes.privateFlags |=
                     WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
             }
@@ -7342,7 +7345,7 @@
                 mPendingMergedConfiguration, mSurfaceControl, mTempInsets, mSurfaceSize,
                 mBlastSurfaceControl);
         if (mSurfaceControl.isValid()) {
-            if (!WindowManagerGlobal.USE_BLAST_ADAPTER) {
+            if (!mUseBLASTAdapter) {
                 mSurface.copyFrom(mSurfaceControl);
             } else {
                 mSurface.transferFrom(getOrCreateBLASTSurface(mSurfaceSize.x,
@@ -9537,7 +9540,7 @@
     }
 
     SurfaceControl getRenderSurfaceControl() {
-        if (WindowManagerGlobal.USE_BLAST_ADAPTER) {
+        if (mUseBLASTAdapter) {
             return mBlastSurfaceControl;
         } else {
             return mSurfaceControl;
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index f501de9..ea8e7388 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -16,8 +16,6 @@
 
 package android.view;
 
-import static android.view.WindowInsets.Type.ime;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -148,7 +146,7 @@
      * @param types The {@link InsetsType}s the application has requested to control.
      * @param durationMillis Duration of animation in
      *                       {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the
-     *                       animation doesn't have a predetermined duration.T his value will be
+     *                       animation doesn't have a predetermined duration. This value will be
      *                       passed to {@link InsetsAnimation#getDurationMillis()}
      * @param interpolator The interpolator used for this animation, or {@code null} if this
      *                     animation doesn't follow an interpolation curve. This value will be
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index f03c4e7..c22b892 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -56,13 +56,7 @@
 public final class WindowManagerGlobal {
     private static final String TAG = "WindowManager";
 
-    private static final String WM_USE_BLAST_ADAPTER_FLAG = "wm_use_blast_adapter";
-
-    /**
-     * This flag controls whether ViewRootImpl will utilize the Blast Adapter
-     * to send buffer updates to SurfaceFlinger
-     */
-    public static final boolean USE_BLAST_ADAPTER = false;
+    private final boolean mUseBLASTAdapter;
 
     /**
      * The user is navigating with keys (not the touch screen), so
@@ -165,6 +159,11 @@
     private Runnable mSystemPropertyUpdater;
 
     private WindowManagerGlobal() {
+        try {
+            mUseBLASTAdapter = getWindowManagerService().useBLAST();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     @UnsupportedAppUsage
@@ -233,6 +232,13 @@
         }
     }
 
+    /**
+     * Whether or not to use BLAST for ViewRootImpl
+     */
+    public boolean useBLAST() {
+        return mUseBLASTAdapter;
+    }
+
     @UnsupportedAppUsage
     public String[] getViewRootNames() {
         synchronized (mLock) {
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 4365d1f..56683dd 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -277,7 +277,7 @@
                     .setStableInsets(Insets.of(stableInsets))
                     .setDisplayCutout(displayCutout.get()).build();
         } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
         }
-        return null;
     }
 }
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index c80a1ae..7f90d57 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -33,11 +33,11 @@
 import android.view.View;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * An EditorInfo describes several attributes of a text editing object
@@ -558,7 +558,7 @@
      *                      editor wants to trim out the first 10 chars, subTextStart should be 10.
      */
     public void setInitialSurroundingSubText(@NonNull CharSequence subText, int subTextStart) {
-        Preconditions.checkNotNull(subText);
+        Objects.requireNonNull(subText);
 
         // Swap selection start and end if necessary.
         final int subTextSelStart = initialSelStart > initialSelEnd
@@ -585,25 +585,35 @@
             return;
         }
 
-        // The input text is too long. Let's try to trim it reasonably. Fundamental rules are:
-        // 1. Text before the cursor is the most important information to IMEs.
-        // 2. Text after the cursor is the second important information to IMEs.
-        // 3. Selected text is the least important information but it shall NEVER be truncated.
-        //    When it is too long, just drop it.
-        //
-        // Source: <TextBeforeCursor><Selection><TextAfterCursor>
-        // Possible results:
-        // 1. <(maybeTrimmedAtHead)TextBeforeCursor><Selection><TextAfterCursor(maybeTrimmedAtTail)>
-        // 2. <(maybeTrimmedAtHead)TextBeforeCursor><TextAfterCursor(maybeTrimmedAtTail)>
-        //
-        final int sourceSelLength = subTextSelEnd - subTextSelStart;
+        trimLongSurroundingText(subText, subTextSelStart, subTextSelEnd);
+    }
+
+    /**
+     * Trims the initial surrounding text when it is over sized. Fundamental trimming rules are:
+     * - The text before the cursor is the most important information to IMEs.
+     * - The text after the cursor is the second important information to IMEs.
+     * - The selected text is the least important information but it shall NEVER be truncated. When
+     *    it is too long, just drop it.
+     *<p><pre>
+     * For example, the subText can be viewed as
+     *     TextBeforeCursor + Selection + TextAfterCursor
+     * The result could be
+     *     1. (maybeTrimmedAtHead)TextBeforeCursor + Selection + TextAfterCursor(maybeTrimmedAtTail)
+     *     2. (maybeTrimmedAtHead)TextBeforeCursor + TextAfterCursor(maybeTrimmedAtTail)</pre>
+     *
+     * @param subText The long text that needs to be trimmed.
+     * @param selStart The text offset of the start of the selection.
+     * @param selEnd The text offset of the end of the selection
+     */
+    private void trimLongSurroundingText(CharSequence subText, int selStart, int selEnd) {
+        final int sourceSelLength = selEnd - selStart;
         // When the selected text is too long, drop it.
         final int newSelLength = (sourceSelLength > MAX_INITIAL_SELECTION_LENGTH)
                 ? 0 : sourceSelLength;
 
         // Distribute rest of length quota to TextBeforeCursor and TextAfterCursor in 4:1 ratio.
-        final int subTextBeforeCursorLength = subTextSelStart;
-        final int subTextAfterCursorLength = subTextLength - subTextSelEnd;
+        final int subTextBeforeCursorLength = selStart;
+        final int subTextAfterCursorLength = subText.length() - selEnd;
         final int maxLengthMinusSelection = MEMORY_EFFICIENT_TEXT_LENGTH - newSelLength;
         final int possibleMaxBeforeCursorLength =
                 Math.min(subTextBeforeCursorLength, (int) (0.8 * maxLengthMinusSelection));
@@ -617,24 +627,23 @@
 
         // We don't want to cut surrogate pairs in the middle. Exam that at the new head and tail.
         if (isCutOnSurrogate(subText,
-                subTextSelStart - newBeforeCursorLength, TrimPolicy.HEAD)) {
+                selStart - newBeforeCursorLength, TrimPolicy.HEAD)) {
             newBeforeCursorHead = newBeforeCursorHead + 1;
             newBeforeCursorLength = newBeforeCursorLength - 1;
         }
         if (isCutOnSurrogate(subText,
-                subTextSelEnd + newAfterCursorLength - 1, TrimPolicy.TAIL)) {
+                selEnd + newAfterCursorLength - 1, TrimPolicy.TAIL)) {
             newAfterCursorLength = newAfterCursorLength - 1;
         }
 
         // Now we know where to trim, compose the initialSurroundingText.
         final int newTextLength = newBeforeCursorLength + newSelLength + newAfterCursorLength;
-        CharSequence newInitialSurroundingText;
+        final CharSequence newInitialSurroundingText;
         if (newSelLength != sourceSelLength) {
             final CharSequence beforeCursor = subText.subSequence(newBeforeCursorHead,
                     newBeforeCursorHead + newBeforeCursorLength);
-
-            final CharSequence afterCursor = subText.subSequence(subTextSelEnd,
-                    subTextSelEnd + newAfterCursorLength);
+            final CharSequence afterCursor = subText.subSequence(selEnd,
+                    selEnd + newAfterCursorLength);
 
             newInitialSurroundingText = TextUtils.concat(beforeCursor, afterCursor);
         } else {
@@ -651,15 +660,16 @@
     }
 
     /**
-     * Get <var>n</var> characters of text before the current cursor position. May be {@code null}
-     * when the protocol is not supported.
+     * Get <var>length</var> characters of text before the current cursor position. May be
+     * {@code null} when the protocol is not supported.
      *
      * @param length The expected length of the text.
      * @param flags Supplies additional options controlling how the text is returned. May be
      * either 0 or {@link InputConnection#GET_TEXT_WITH_STYLES}.
      * @return the text before the cursor position; the length of the returned text might be less
-     * than <var>n</var>. When there is no text before the cursor, an empty string will be returned.
-     * It could also be {@code null} when the editor or system could not support this protocol.
+     * than <var>length</var>. When there is no text before the cursor, an empty string will be
+     * returned. It could also be {@code null} when the editor or system could not support this
+     * protocol.
      */
     @Nullable
     public CharSequence getInitialTextBeforeCursor(int length, int flags) {
@@ -667,8 +677,8 @@
     }
 
     /**
-     * Gets the selected text, if any. May be {@code null} when no text is selected or the selected
-     * text is way too long.
+     * Gets the selected text, if any. May be {@code null} when the protocol is not supported or the
+     * selected text is way too long.
      *
      * @param flags Supplies additional options controlling how the text is returned. May be
      * either 0 or {@link InputConnection#GET_TEXT_WITH_STYLES}.
@@ -693,15 +703,16 @@
     }
 
     /**
-     * Get <var>n</var> characters of text after the current cursor position. May be {@code null}
-     * when the protocol is not supported.
+     * Get <var>length</var> characters of text after the current cursor position. May be
+     * {@code null} when the protocol is not supported.
      *
      * @param length The expected length of the text.
      * @param flags Supplies additional options controlling how the text is returned. May be
      * either 0 or {@link InputConnection#GET_TEXT_WITH_STYLES}.
      * @return the text after the cursor position; the length of the returned text might be less
-     * than <var>n</var>. When there is no text after the cursor, an empty string will be returned.
-     * It could also be {@code null} when the editor or system could not support this protocol.
+     * than <var>length</var>. When there is no text after the cursor, an empty string will be
+     * returned. It could also be {@code null} when the editor or system could not support this
+     * protocol.
      */
     @Nullable
     public CharSequence getInitialTextAfterCursor(int length, int flags) {
@@ -863,7 +874,6 @@
         return 0;
     }
 
-    // TODO(b/148035211): Unit tests for this class
     static final class InitialSurroundingText implements Parcelable {
         @Nullable final CharSequence mSurroundingText;
         final int mSelectionHead;
diff --git a/core/java/android/view/textclassifier/TextClassificationSession.java b/core/java/android/view/textclassifier/TextClassificationSession.java
index 4329a20..fed3dbf 100644
--- a/core/java/android/view/textclassifier/TextClassificationSession.java
+++ b/core/java/android/view/textclassifier/TextClassificationSession.java
@@ -16,6 +16,7 @@
 
 package android.view.textclassifier;
 
+import android.annotation.NonNull;
 import android.annotation.WorkerThread;
 import android.view.textclassifier.SelectionEvent.InvocationMethod;
 
@@ -23,6 +24,8 @@
 
 import java.util.Objects;
 
+import sun.misc.Cleaner;
+
 /**
  * Session-aware TextClassifier.
  */
@@ -35,6 +38,7 @@
     private final SelectionEventHelper mEventHelper;
     private final TextClassificationSessionId mSessionId;
     private final TextClassificationContext mClassificationContext;
+    private final Cleaner mCleaner;
 
     private boolean mDestroyed;
 
@@ -44,6 +48,8 @@
         mSessionId = new TextClassificationSessionId();
         mEventHelper = new SelectionEventHelper(mSessionId, mClassificationContext);
         initializeRemoteSession();
+        // This ensures destroy() is called if the client forgot to do so.
+        mCleaner = Cleaner.create(this, new CleanerRunnable(mEventHelper, mDelegate));
     }
 
     @Override
@@ -114,8 +120,7 @@
 
     @Override
     public void destroy() {
-        mEventHelper.endSession();
-        mDelegate.destroy();
+        mCleaner.clean();
         mDestroyed = true;
     }
 
@@ -258,4 +263,25 @@
             }
         }
     }
+
+    // We use a static nested class here to avoid retaining the object reference of the outer
+    // class. Otherwise. the Cleaner would never be triggered.
+    private static class CleanerRunnable implements Runnable {
+        @NonNull
+        private final SelectionEventHelper mEventHelper;
+        @NonNull
+        private final TextClassifier mDelegate;
+
+        CleanerRunnable(
+                @NonNull SelectionEventHelper eventHelper, @NonNull TextClassifier delegate) {
+            mEventHelper = Objects.requireNonNull(eventHelper);
+            mDelegate = Objects.requireNonNull(delegate);
+        }
+
+        @Override
+        public void run() {
+            mEventHelper.endSession();
+            mDelegate.destroy();
+        }
+    }
 }
diff --git a/core/java/android/view/textclassifier/TextClassificationSessionId.java b/core/java/android/view/textclassifier/TextClassificationSessionId.java
index f90e6b2..0b6fba2 100644
--- a/core/java/android/view/textclassifier/TextClassificationSessionId.java
+++ b/core/java/android/view/textclassifier/TextClassificationSessionId.java
@@ -17,6 +17,8 @@
 package android.view.textclassifier;
 
 import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -28,7 +30,10 @@
  * This class represents the id of a text classification session.
  */
 public final class TextClassificationSessionId implements Parcelable {
-    private final @NonNull String mValue;
+    @NonNull
+    private final String mValue;
+    @NonNull
+    private final IBinder mToken;
 
     /**
      * Creates a new instance.
@@ -36,7 +41,7 @@
      * @hide
      */
     public TextClassificationSessionId() {
-        this(UUID.randomUUID().toString());
+        this(UUID.randomUUID().toString(), new Binder());
     }
 
     /**
@@ -46,34 +51,28 @@
      *
      * @hide
      */
-    public TextClassificationSessionId(@NonNull String value) {
-        mValue = value;
+    public TextClassificationSessionId(@NonNull String value, @NonNull IBinder token) {
+        mValue = Objects.requireNonNull(value);
+        mToken = Objects.requireNonNull(token);
+    }
+
+    /** @hide */
+    @NonNull
+    public IBinder getToken() {
+        return mToken;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        TextClassificationSessionId that = (TextClassificationSessionId) o;
+        return Objects.equals(mValue, that.mValue) && Objects.equals(mToken, that.mToken);
     }
 
     @Override
     public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + mValue.hashCode();
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        TextClassificationSessionId other = (TextClassificationSessionId) obj;
-        if (!mValue.equals(other.mValue)) {
-            return false;
-        }
-        return true;
+        return Objects.hash(mValue, mToken);
     }
 
     @Override
@@ -84,6 +83,7 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeString(mValue);
+        parcel.writeStrongBinder(mToken);
     }
 
     @Override
@@ -96,28 +96,18 @@
      *
      * @return The flattened id.
      */
-    public @NonNull String flattenToString() {
+    @NonNull
+    public String flattenToString() {
         return mValue;
     }
 
-    /**
-     * Unflattens a print job id from a string.
-     *
-     * @param string The string.
-     * @return The unflattened id, or null if the string is malformed.
-     *
-     * @hide
-     */
-    public static @NonNull TextClassificationSessionId unflattenFromString(@NonNull String string) {
-        return new TextClassificationSessionId(string);
-    }
-
-    public static final @android.annotation.NonNull Parcelable.Creator<TextClassificationSessionId> CREATOR =
+    @NonNull
+    public static final Parcelable.Creator<TextClassificationSessionId> CREATOR =
             new Parcelable.Creator<TextClassificationSessionId>() {
                 @Override
                 public TextClassificationSessionId createFromParcel(Parcel parcel) {
                     return new TextClassificationSessionId(
-                            Objects.requireNonNull(parcel.readString()));
+                            parcel.readString(), parcel.readStrongBinder());
                 }
 
                 @Override
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 417e23f..f3b6d29 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -38,10 +38,8 @@
             IVoiceInteractor interactor);
     boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags);
     boolean hideSessionFromSession(IBinder token);
-    int startVoiceActivity(IBinder token, in Intent intent, String resolvedType,
-            String callingFeatureId);
-    int startAssistantActivity(IBinder token, in Intent intent, String resolvedType,
-            String callingFeatureId);
+    int startVoiceActivity(IBinder token, in Intent intent, String resolvedType);
+    int startAssistantActivity(IBinder token, in Intent intent, String resolvedType);
     void setKeepAwake(IBinder token, boolean keepAwake);
     void closeSystemDialogs(IBinder token);
     void finish(IBinder token);
diff --git a/core/java/com/android/internal/logging/UiEventLogger.java b/core/java/com/android/internal/logging/UiEventLogger.java
index 48d2bc2..67ffd4d 100644
--- a/core/java/com/android/internal/logging/UiEventLogger.java
+++ b/core/java/com/android/internal/logging/UiEventLogger.java
@@ -56,8 +56,8 @@
      * @param event an enum implementing UiEventEnum interface.
      * @param uid the uid of the relevant app, if known (0 otherwise).
      * @param packageName the package name of the relevant app, if known (null otherwise).
-     * @param instance An identifier obtained from an InstanceIdSequence.
+     * @param instance An identifier obtained from an InstanceIdSequence. If null, reduces to log().
      */
     void logWithInstanceId(@NonNull UiEventEnum event, int uid, @Nullable String packageName,
-            @NonNull InstanceId instance);
+            @Nullable InstanceId instance);
 }
diff --git a/core/java/com/android/internal/logging/UiEventLoggerImpl.java b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
index 785b2ed..4d171ec 100644
--- a/core/java/com/android/internal/logging/UiEventLoggerImpl.java
+++ b/core/java/com/android/internal/logging/UiEventLoggerImpl.java
@@ -41,9 +41,11 @@
     public void logWithInstanceId(UiEventEnum event, int uid, String packageName,
             InstanceId instance) {
         final int eventID = event.getId();
-        if (eventID > 0) {
+        if ((eventID > 0)  && (instance != null)) {
             FrameworkStatsLog.write(FrameworkStatsLog.UI_EVENT_REPORTED, eventID, uid, packageName,
                     instance.getId());
+        } else {
+            log(event, uid, packageName);
         }
     }
 }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 76e7e19..8959d6f 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -360,7 +360,6 @@
         "android_view_RenderNode.cpp",
         "android_util_PathParser.cpp",
 
-        "android/graphics/AnimatedImageDrawable.cpp",
         "android/graphics/Bitmap.cpp",
         "android/graphics/BitmapFactory.cpp",
         "android/graphics/ByteBufferStreamAdaptor.cpp",
@@ -436,6 +435,7 @@
 
                 "android_view_TextureLayer.cpp",
                 "android_view_ThreadedRenderer.cpp",
+                "android/graphics/AnimatedImageDrawable.cpp",
                 "android/graphics/BitmapRegionDecoder.cpp",
                 "android/graphics/GIFMovie.cpp",
                 "android/graphics/Movie.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index d0e8fd3..b47b7e3 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -209,9 +209,11 @@
 static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath";
 
 // Feature flag name for running the JIT in Zygote experiment, b/119800099.
-static const char* ENABLE_APEX_IMAGE = "enable_apex_image";
-// Flag to pass to the runtime when using the apex image.
-static const char* kApexImageOption = "-Ximage:/system/framework/apex.art";
+// TODO: Rename the server-level flag or remove.
+static const char* ENABLE_JITZYGOTE_IMAGE = "enable_apex_image";
+// Flag to pass to the runtime when using the JIT Zygote image.
+static const char* kJitZygoteImageOption =
+        "-Ximage:boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof";
 
 // Feature flag name for disabling lock profiling.
 static const char* DISABLE_LOCK_PROFILING = "disable_lock_profiling";
@@ -689,16 +691,16 @@
         addOption("-Xjitsaveprofilinginfo");
     }
 
-    std::string use_apex_image_flag =
-        server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
-                                                             ENABLE_APEX_IMAGE,
-                                                             /*default_value=*/ "");
+    std::string use_jitzygote_image_flag =
+            server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
+                                                                 ENABLE_JITZYGOTE_IMAGE,
+                                                                 /*default_value=*/"");
     // Use the APEX boot image for boot class path profiling to get JIT samples on BCP methods.
     // Also use the APEX boot image if it's explicitly enabled via configuration flag.
-    const bool use_apex_image = profile_boot_class_path || (use_apex_image_flag == "true");
+    const bool use_apex_image = profile_boot_class_path || (use_jitzygote_image_flag == "true");
     if (use_apex_image) {
-        addOption(kApexImageOption);
-        ALOGI("Using Apex boot image: '%s'\n", kApexImageOption);
+        ALOGI("Using JIT Zygote image: '%s'\n", kJitZygoteImageOption);
+        addOption(kJitZygoteImageOption);
     } else if (parseRuntimeOption("dalvik.vm.boot-image", bootImageBuf, "-Ximage:")) {
         ALOGI("Using dalvik.vm.boot-image: '%s'\n", bootImageBuf);
     } else {
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 571a3387..c49c3a0 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -40,6 +40,7 @@
 extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env);
 extern int register_android_graphics_Graphics(JNIEnv* env);
 extern int register_android_graphics_ImageDecoder(JNIEnv*);
+extern int register_android_graphics_Interpolator(JNIEnv* env);
 extern int register_android_graphics_MaskFilter(JNIEnv* env);
 extern int register_android_graphics_NinePatch(JNIEnv*);
 extern int register_android_graphics_PathEffect(JNIEnv* env);
@@ -115,6 +116,7 @@
         {"android.graphics.FontFamily", REG_JNI(register_android_graphics_FontFamily)},
         {"android.graphics.Graphics", REG_JNI(register_android_graphics_Graphics)},
         {"android.graphics.ImageDecoder", REG_JNI(register_android_graphics_ImageDecoder)},
+        {"android.graphics.Interpolator", REG_JNI(register_android_graphics_Interpolator)},
         {"android.graphics.MaskFilter", REG_JNI(register_android_graphics_MaskFilter)},
         {"android.graphics.Matrix", REG_JNI(register_android_graphics_Matrix)},
         {"android.graphics.NinePatch", REG_JNI(register_android_graphics_NinePatch)},
diff --git a/core/jni/android_os_incremental_IncrementalManager.cpp b/core/jni/android_os_incremental_IncrementalManager.cpp
index d41e982..44bff01 100644
--- a/core/jni/android_os_incremental_IncrementalManager.cpp
+++ b/core/jni/android_os_incremental_IncrementalManager.cpp
@@ -37,10 +37,28 @@
     return (jboolean)IncFs_IsIncFsPath(path.c_str());
 }
 
-static const JNINativeMethod method_table[] = {
-        {"nativeIsEnabled", "()Z", (void*)nativeIsEnabled},
-        {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z", (void*)nativeIsIncrementalPath},
-};
+static jbyteArray nativeUnsafeGetFileSignature(JNIEnv* env, jobject clazz, jstring javaPath) {
+    ScopedUtfChars path(env, javaPath);
+
+    char signature[INCFS_MAX_SIGNATURE_SIZE];
+    size_t size = sizeof(signature);
+    if (IncFs_UnsafeGetSignatureByPath(path.c_str(), signature, &size) < 0) {
+        return nullptr;
+    }
+
+    jbyteArray result = env->NewByteArray(size);
+    if (result != nullptr) {
+        env->SetByteArrayRegion(result, 0, size, (const jbyte*)signature);
+    }
+    return result;
+}
+
+static const JNINativeMethod method_table[] = {{"nativeIsEnabled", "()Z", (void*)nativeIsEnabled},
+                                               {"nativeIsIncrementalPath", "(Ljava/lang/String;)Z",
+                                                (void*)nativeIsIncrementalPath},
+                                               {"nativeUnsafeGetFileSignature",
+                                                "(Ljava/lang/String;)[B",
+                                                (void*)nativeUnsafeGetFileSignature}};
 
 int register_android_os_incremental_IncrementalManager(JNIEnv* env) {
     return jniRegisterNativeMethods(env, "android/os/incremental/IncrementalManager",
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 92941b8..7290e30 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -611,6 +611,12 @@
 
   // Set the jemalloc decay time to 1.
   mallopt(M_DECAY_TIME, 1);
+
+  // Maybe initialize GWP-ASan here. Must be called after
+  // mallopt(M_SET_ZYGOTE_CHILD).
+  bool ForceEnableGwpAsan = false;
+  android_mallopt(M_INITIALIZE_GWP_ASAN, &ForceEnableGwpAsan,
+                  sizeof(ForceEnableGwpAsan));
 }
 
 static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 4625418..53aa2bb 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -714,6 +714,16 @@
 
     // ACTION: Battery feature runtime event
     ACTION_BATTERY_OPTION_RUNTIME_EVENT = 1733;
+
+    // ACTION: Settings > Developer Options > Toggle on Wireless debugging
+    // CATEGORY: SETTINGS
+    // OS: R
+    ACTION_ADB_WIRELESS_ON = 1734;
+
+    // ACTION: Settings > Developer Options > Toggle off Wireless debugging
+    // CATEGORY: SETTINGS
+    // OS: R
+    ACTION_ADB_WIRELESS_OFF = 1735;
 }
 
 /**
@@ -2591,4 +2601,20 @@
     // OPEN: Settings > Notifications > (app or conversations) > conversation
     NOTIFICATION_CONVERSATION_SETTINGS = 1830;
 
+    // OPEN: Settings > Developer Options > Wireless debugging
+    // CATEGORY: SETTINGS
+    // OS: R
+    SETTINGS_ADB_WIRELESS = 1831;
+
+    // OPEN: Settings > Developer Options > Wireless debugging
+    //   > Pair device with pairing code > Pairing code dialog
+    // CATEGORY: SETTINGS
+    // OS: R
+    ADB_WIRELESS_DEVICE_PAIRING_DIALOG = 1832;
+
+    // OPEN: Settings > Developer Options > Wireless debugging
+    //   > Pair device with QR code > Scan QR code > Pairing device dialog
+    // CATEGORY: SETTINGS
+    // OS: R
+    ADB_WIRELESS_DEVICE_QR_PAIRING_DIALOG = 1833;
 }
diff --git a/core/proto/android/content/locusid.proto b/core/proto/android/content/locusid.proto
new file mode 100644
index 0000000..4f0ce6b
--- /dev/null
+++ b/core/proto/android/content/locusid.proto
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package android.content;
+
+option java_multiple_files = true;
+
+// On disk representation of android.content.LocusId. Currently used by
+// com.android.server.people.ConversationInfoProto.
+message LocusIdProto {
+  optional string locus_id = 1;
+}
diff --git a/core/proto/android/server/peopleservice.proto b/core/proto/android/server/peopleservice.proto
new file mode 100644
index 0000000..294b6ef
--- /dev/null
+++ b/core/proto/android/server/peopleservice.proto
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package com.android.server.people;
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/content/locusid.proto";
+
+// On disk data of conversation infos for a user and app package.
+message ConversationInfosProto {
+
+  // The series of conversation infos for a user and app package.
+  repeated ConversationInfoProto conversation_infos = 1;
+}
+
+// Individual conversation info (com.android.server.people.data.ConversationInfo) for a user
+// and app package.
+message ConversationInfoProto {
+
+  // The conversation's shortcut id.
+  optional string shortcut_id = 1;
+
+  // The conversation's locus id.
+  optional .android.content.LocusIdProto locus_id_proto = 2;
+
+  // The URI of the contact in the conversation.
+  optional string contact_uri = 3;
+
+  // The notification channel id of the conversation.
+  optional string notification_channel_id = 4;
+
+  // Integer representation of shortcut bit flags.
+  optional int32 shortcut_flags = 5;
+
+  // Integer representation of conversation bit flags.
+  optional int32 conversation_flags = 6;
+}
+
+// Individual event (com.android.server.people.data.Event).
+message PeopleEventProto {
+
+  // For valid values, refer to java class documentation.
+  optional int32 event_type = 1;
+
+  optional int64 time = 2;
+
+  // The duration of the event. Should only be set for some event_types. Refer to java class
+  // documentation for details.
+  optional int32 duration = 3;
+}
+
+// Index of events' time distributions (com.android.server.people.data.EventIndex).
+message PeopleEventIndexProto {
+    // Each long value in event_bitmaps represents a time slot, there should be 4 values. Further
+    // details can be found in class documentation.
+    repeated int64 event_bitmaps = 1;
+
+    optional int64 last_updated_time = 2;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ff69671..71a42e4 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -96,6 +96,7 @@
     <protected-broadcast android:name="android.intent.action.OVERLAY_PRIORITY_CHANGED" />
     <protected-broadcast android:name="android.intent.action.MY_PACKAGE_SUSPENDED" />
     <protected-broadcast android:name="android.intent.action.MY_PACKAGE_UNSUSPENDED" />
+    <protected-broadcast android:name="android.intent.action.LOAD_DATA" />
 
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGING" />
@@ -2376,6 +2377,7 @@
          @hide -->
     <permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
         android:protectionLevel="signature|installer|telephony" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
 
     <!-- Allows interaction across profiles in the same profile group. -->
     <permission android:name="android.permission.INTERACT_ACROSS_PROFILES"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index d6c5a13..5e89533 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Skakel werkprofiel aan?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Jou werkprogramme, kennisgewings, data en ander werkprofielkenmerke sal aangeskakel word"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Skakel aan"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Program is nie beskikbaar nie"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is nie op die oomblik beskikbaar nie."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Hierdie program is vir \'n ouer weergawe van Android gebou en sal dalk nie behoorlik werk nie. Probeer kyk vir opdaterings, of kontak die ontwikkelaar."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Kyk vir opdatering"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Jy het nuwe boodskappe"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index e9409fb..b637a63 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"የስራ መገለጫ ይብራ?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"የእርስዎ የስራ መተግበሪያዎች፣ ማሳወቂያዎች፣ ውሂብ እና ሌሎች የስራ መገለጫ ባህሪያት ይበራሉ"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"አብራ"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"መተግበሪያ አይገኝም"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> አሁን አይገኝም።"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ይህ መተግበሪያ ለቆየ የAndroid ስሪት ነው የተገነባው፣ እና በአግባቡ ላይሰራ ይችላል። ዝማኔዎች ካሉ ለመመልከት ይሞክሩ፣ ወይም ደግሞ ገንቢውን ያነጋግሩ።"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ዝማኔ ካለ አረጋግጥ"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"አዲስ መልዕክቶች አለዎት"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 23ffd7b..002d61e 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -915,7 +915,7 @@
     <string name="factorytest_no_action" msgid="339252838115675515">"‏لم يتم العثور على أي حزمة توفر إجراء FACTORY_TEST."</string>
     <string name="factorytest_reboot" msgid="2050147445567257365">"إعادة تشغيل"</string>
     <string name="js_dialog_title" msgid="7464775045615023241">"تعرض الصفحة في \"<xliff:g id="TITLE">%s</xliff:g>\":"</string>
-    <string name="js_dialog_title_default" msgid="3769524569903332476">"جافا سكريبت"</string>
+    <string name="js_dialog_title_default" msgid="3769524569903332476">"JavaScript"</string>
     <string name="js_dialog_before_unload_title" msgid="7012587995876771246">"تأكيد الانتقال"</string>
     <string name="js_dialog_before_unload_positive_button" msgid="4274257182303565509">"مغادرة هذه الصفحة"</string>
     <string name="js_dialog_before_unload_negative_button" msgid="3873765747622415310">"البقاء في هذه الصفحة"</string>
@@ -1395,7 +1395,7 @@
     <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"يمكنك إجراء إعادة ضبط على الإعدادات الأصلية لإيقاف وضع \"مفعِّل اختبار\"."</string>
     <string name="console_running_notification_title" msgid="6087888939261635904">"وحدة التحكّم التسلسلية مفعّلة"</string>
     <string name="console_running_notification_message" msgid="7892751888125174039">"الأداء متأثر. لإيقاف وحدة التحكّم، تحقّق من برنامج الإقلاع."</string>
-    <string name="usb_contaminant_detected_title" msgid="4359048603069159678">"‏السوائل والشوائب في منفذ USB"</string>
+    <string name="usb_contaminant_detected_title" msgid="4359048603069159678">"‏السوائل أو الشوائب في منفذ USB"</string>
     <string name="usb_contaminant_detected_message" msgid="7346100585390795743">"‏تمّ إيقاف منفذ USB تلقائيًا. انقُر لمعرفة المزيد من المعلومات."</string>
     <string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"‏مسموح باستخدام منفذ USB"</string>
     <string name="usb_contaminant_not_detected_message" msgid="892863190942660462">"لم يَعُد الهاتف يكتشف سوائل أو شوائب."</string>
@@ -1981,6 +1981,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"تفعيل الملف الشخصي للعمل؟"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"سيتم تفعيل تطبيقات العمل التي تستخدمها والإشعارات والبيانات وغيرها من ميزات الملف الشخصي للعمل"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"تشغيل"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"التطبيق غير متاح"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> غير متاح الآن."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‏تمّ إنشاء هذا التطبيق لإصدار قديم من Android وقد لا يعمل بشكل صحيح. جرِّب البحث عن تحديثات أو الاتصال بمطوّر البرامج."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"البحث عن تحديث"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"لديك رسائل جديدة"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index a09c377..bdf8fcf 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"কৰ্মস্থানৰ প্ৰ\'ফাইল অন কৰিবনে?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"আপোনাৰ কৰ্মস্থানৰ এপসমূহ, জাননীসমূহ, ডেটা আৰু কৰ্মস্থানৰ প্ৰ\'ফাইলৰ অইন সুবিধাসমূহ অন কৰা হ\'ব"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"অন কৰক"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"এপ্‌টো উপলব্ধ নহয়"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"এই মুহূৰ্তত <xliff:g id="APP_NAME">%1$s</xliff:g> উপলব্ধ নহয়।"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"এই এপটো Androidৰ এটা পুৰণা সংস্কৰণৰ বাবে প্ৰস্তুত কৰা হৈছিল, আৰু ই বিচৰাধৰণে কাম নকৰিবও পাৰে। ইয়াৰ আপডে’ট আছে নেকি চাওক, বা বিকাশকৰ্তাৰ সৈতে যোগাযোগ কৰক।"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"আপডে’ট আছে নেকি চাওক"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"আপুনি নতুন বার্তা লাভ কৰিছে"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index b1209b1..6f13024 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"İş profili aktiv edilsin?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"İş tətbiqləri, bildirişləri, data və digər iş profili funksiyaları aktiv ediləcək"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Aktivləşdirin"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Tətbiq əlçatan deyil"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> hazırda əlçatan deyil."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Bu tətbiq köhnə Android versiyası üçün hazırlanıb və düzgün işləməyə bilər. Güncəlləməni yoxlayın və ya developer ilə əlaqə saxlayın."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Güncəlləməni yoxlayın"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Yeni mesajlarınız var"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 5600882..862495a 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -1885,6 +1885,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Da uključimo profil za Work?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Uključiće se poslovne aplikacije, obaveštenja, podaci i druge funkcije profila za Work"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Uključi"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno nije dostupna."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ova aplikacija je napravljena za stariju verziju Android-a, pa možda neće raditi ispravno. Potražite ažuriranja ili kontaktirajte programera."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Potraži ažuriranje"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Imate nove poruke"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index fb80e4b..8a02da2 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -1917,6 +1917,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Уключыць працоўны профіль?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Будуць уключаны вашы рабочыя праграмы, апавяшчэнні, даныя і іншыя функцыі працоўнага профілю"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Уключыць"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Праграма недаступная"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" цяпер недаступная."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Гэта праграма была створана для больш старой версіі Android і можа не працаваць належным чынам. Праверце наяўнасць абнаўленняў або звярніцеся да распрацоўшчыка."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Праверыць на наяўнасць абнаўленняў"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"У вас ёсць новыя паведамленні"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 2892853..f7830c7 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Вкл. на служ. потр. профил?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Служебните ви приложения, известия и данни, както и другите функции на служебния потребителски профил ще бъдат включени"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Включване"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Приложението не е достъпно"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"В момента няма достъп до <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Това приложение бе създадено за по-стара версия на Android и може да не работи правилно. Опитайте да проверите за актуализации или се свържете с програмиста."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Проверка за актуализация"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Имате нови съобщения"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 4c95081..af50b33 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -1297,7 +1297,7 @@
     <string name="no_permissions" msgid="5729199278862516390">"কোনো অনুমতির প্রয়োজন নেই"</string>
     <string name="perm_costs_money" msgid="749054595022779685">"এর জন্য অর্থপ্রদান করতে হতে পারে"</string>
     <string name="dlg_ok" msgid="5103447663504839312">"ঠিক আছে"</string>
-    <string name="usb_charging_notification_title" msgid="1674124518282666955">"এই ডিভাইসটি USB এর মাধ্যমে চার্জ করুন"</string>
+    <string name="usb_charging_notification_title" msgid="1674124518282666955">"এই ডিভাইসটি USB দিয়ে চার্জ করা হচ্ছে"</string>
     <string name="usb_supplying_notification_title" msgid="5378546632408101811">"সংযুক্ত ডিভাইসটি USB এর মাধ্যমে চার্জ করা হচ্ছে"</string>
     <string name="usb_mtp_notification_title" msgid="1065989144124499810">"USB ফাইল ট্রান্সফার চালু করা হয়েছে"</string>
     <string name="usb_ptp_notification_title" msgid="5043437571863443281">"USB এর মাধ্যমে PTP চালু করা হয়েছে"</string>
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"কাজের প্রোফাইল চালু করবেন?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"আপনার কাজের অ্যাপ, বিজ্ঞপ্তি, ডেটা এবং কাজের প্রোফাইলের অন্যান্য বৈশিষ্ট্য চালু করা হবে"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"চালু করুন"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"অ্যাপ পাওয়া যাচ্ছে না"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"এই মুহূর্তে <xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপ পাওয়া যাচ্ছে না।"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"এই অ্যাপটি Android এর একটি পুরনো ভার্সনের জন্য তৈরি করা হয়েছিল, তাই এখানে সেটি ঠিকমতো কাজ নাও করতে পারে। আপডেট পাওয়া যাচ্ছে কিনা দেখুন বা ডেভেলপারের সাথে যোগাযোগ করুন।"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"আপডেট পাওয়া যাচ্ছে কিনা দেখুন"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"আপনার নতুন মেসেজ আছে"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index eaa212d..d436c5f 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -1887,6 +1887,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Uključiti radni profil?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Uključit će se poslovne aplikacije, obavještenja, podaci i druge funkcije radnog profila"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Uključi"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno nije dostupna."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ova aplikacija je pravljena za stariju verziju Androida i možda neće ispravno raditi. Provjerite jesu li dostupna ažuriranja ili kontaktirajte programera."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Provjeri je li dostupno ažuriranje"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Imate nove poruke"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 0b58cae..f3b416c 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -182,11 +182,11 @@
       <item quantity="one">Autoritat de certificació instal·lada</item>
     </plurals>
     <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Per un tercer desconegut"</string>
-    <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Per l\'administrador del teu perfil professional"</string>
+    <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"Per l\'administrador del teu perfil de treball"</string>
     <string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"Per <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
-    <string name="work_profile_deleted" msgid="5891181538182009328">"S\'ha suprimit el perfil professional"</string>
-    <string name="work_profile_deleted_details" msgid="3773706828364418016">"Falta l\'aplicació d\'administració del perfil professional o està malmesa. Com a conseqüència, s\'han suprimit el teu perfil professional i les dades relacionades. Contacta amb l\'administrador per obtenir ajuda."</string>
-    <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"El teu perfil professional ja no està disponible en aquest dispositiu"</string>
+    <string name="work_profile_deleted" msgid="5891181538182009328">"S\'ha suprimit el perfil de treball"</string>
+    <string name="work_profile_deleted_details" msgid="3773706828364418016">"Falta l\'aplicació d\'administració del perfil de treball o està malmesa. Com a conseqüència, s\'han suprimit el teu perfil de treball i les dades relacionades. Contacta amb l\'administrador per obtenir ajuda."</string>
+    <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"El teu perfil de treball ja no està disponible en aquest dispositiu"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Has intentat introduir la contrasenya massa vegades"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"L\'administrador ha cedit el dispositiu per a ús personal"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"El dispositiu està gestionat"</string>
@@ -280,7 +280,7 @@
     <string name="safeMode" msgid="8974401416068943888">"Mode segur"</string>
     <string name="android_system_label" msgid="5974767339591067210">"Sistema Android"</string>
     <string name="user_owner_label" msgid="8628726904184471211">"Canvia al perfil personal"</string>
-    <string name="managed_profile_label" msgid="7316778766973512382">"Canvia al perfil professional"</string>
+    <string name="managed_profile_label" msgid="7316778766973512382">"Canvia al perfil de treball"</string>
     <string name="permgrouplab_contacts" msgid="4254143639307316920">"Contactes"</string>
     <string name="permgroupdesc_contacts" msgid="9163927941244182567">"accedir als contactes"</string>
     <string name="permgrouplab_location" msgid="1858277002233964394">"Ubicació"</string>
@@ -1406,8 +1406,8 @@
     <string name="deny" msgid="6632259981847676572">"Denega"</string>
     <string name="permission_request_notification_title" msgid="1810025922441048273">"Permís sol·licitat"</string>
     <string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"S\'ha sol·licitat permís\nper al compte <xliff:g id="ACCOUNT">%s</xliff:g>."</string>
-    <string name="forward_intent_to_owner" msgid="4620359037192871015">"Estàs utilitzant aquesta aplicació fora del perfil professional."</string>
-    <string name="forward_intent_to_work" msgid="3620262405636021151">"Estàs utilitzant l\'aplicació al perfil professional."</string>
+    <string name="forward_intent_to_owner" msgid="4620359037192871015">"Estàs utilitzant aquesta aplicació fora del perfil de treball."</string>
+    <string name="forward_intent_to_work" msgid="3620262405636021151">"Estàs utilitzant l\'aplicació al perfil de treball."</string>
     <string name="input_method_binding_label" msgid="1166731601721983656">"Mètode d\'introducció de text"</string>
     <string name="sync_binding_label" msgid="469249309424662147">"Sincronització"</string>
     <string name="accessibility_binding_label" msgid="1974602776545801715">"Accessibilitat"</string>
@@ -1817,7 +1817,7 @@
     <string name="stk_cc_ss_to_dial_video" msgid="1324194624384312664">"La sol·licitud SS s\'ha canviat per una videotrucada"</string>
     <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"La sol·licitud SS s\'ha canviat per una sol·licitud USSD"</string>
     <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"S\'ha canviat a una nova sol·licitud SS"</string>
-    <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil professional"</string>
+    <string name="notification_work_profile_content_description" msgid="5296477955677725799">"Perfil de treball"</string>
     <string name="notification_alerted_content_description" msgid="6139691253611265992">"S\'ha enviat una alerta"</string>
     <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Desplega"</string>
     <string name="expand_button_content_description_expanded" msgid="7484217944948667489">"Replega"</string>
@@ -1850,15 +1850,17 @@
     <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> no està disponible en aquests moments. Aquesta opció es gestiona a <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
     <string name="app_suspended_more_details" msgid="211260942831587014">"Més informació"</string>
     <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Reactiva l\'aplicació"</string>
-    <string name="work_mode_off_title" msgid="5503291976647976560">"Activar el perfil professional?"</string>
-    <string name="work_mode_off_message" msgid="8417484421098563803">"S\'activaran les teves aplicacions de treball, les notificacions, les dades i altres funcions del perfil professional"</string>
+    <string name="work_mode_off_title" msgid="5503291976647976560">"Activar el perfil de treball?"</string>
+    <string name="work_mode_off_message" msgid="8417484421098563803">"S\'activaran les teves aplicacions de treball, les notificacions, les dades i altres funcions del perfil de treball"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Activa"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"L\'aplicació no està disponible"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Ara mateix, <xliff:g id="APP_NAME">%1$s</xliff:g> no està disponible."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Aquesta aplicació es va crear per a una versió antiga d\'Android i pot ser que no funcioni correctament. Prova de cercar actualitzacions o contacta amb el desenvolupador."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Cerca actualitzacions"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Tens missatges nous"</string>
     <string name="new_sms_notification_content" msgid="3197949934153460639">"Obre l\'aplicació d\'SMS per veure\'ls"</string>
     <string name="profile_encrypted_title" msgid="9001208667521266472">"Algunes funcions poden ser limitades"</string>
-    <string name="profile_encrypted_detail" msgid="5279730442756849055">"Perfil professional bloquejat"</string>
+    <string name="profile_encrypted_detail" msgid="5279730442756849055">"Perfil de treball bloquejat"</string>
     <string name="profile_encrypted_message" msgid="1128512616293157802">"Toca per desbloquejar el perfil"</string>
     <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"S\'ha connectat a <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string>
     <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Toca per veure els fitxers"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 5874c24..754a020 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1917,6 +1917,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Zapnout pracovní profil?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Vaše pracovní aplikace, oznámení, data a ostatní funkce pracovního účtu budou zapnuty"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Zapnout"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Aplikace není k dispozici"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> v tuto chvíli není k dispozici."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Tato aplikace byla vytvořena pro starší verzi systému Android a nemusí fungovat správně. Zkuste vyhledat aktualizace, případně kontaktujte vývojáře."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Zkontrolovat aktualizace"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Máte nové zprávy"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index eb7ad1b..733c7f0 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Skal arbejdsprofilen slås til?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Dine arbejdsapps, notifikationer, data og andre funktioner til din arbejdsprofil deaktiveres"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Slå til"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Appen er ikke tilgængelig"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ikke tilgængelig lige nu."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Denne app er lavet til en ældre version af Android og fungerer muligvis ikke korrekt. Prøv at søge efter opdateringer, eller kontakt udvikleren."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Søg efter opdatering"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Du har nye beskeder"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 6b01af1..9a4c606 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Arbeitsprofil aktivieren?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Deine geschäftlichen Apps, Benachrichtigungen, Daten und andere Funktionen des Arbeitsprofils werden aktiviert"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Aktivieren"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"App ist nicht verfügbar"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ist derzeit nicht verfügbar."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Diese App wurde für eine ältere Android-Version entwickelt und funktioniert möglicherweise nicht mehr richtig. Prüfe, ob Updates verfügbar sind oder kontaktiere den Entwickler."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Auf Updates prüfen"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Du hast neue Nachrichten"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 61ca692..30a8d61 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Ενεργοποίηση προφίλ εργασίας;"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Οι εφαρμογές, οι ειδοποιήσεις και τα δεδομένα εργασίας σας, καθώς και άλλες λειτουργίες του προφίλ εργασίας, θα ενεργοποιηθούν"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Ενεργοποίηση"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Η εφαρμογή δεν είναι διαθέσιμη"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> δεν είναι διαθέσιμη αυτήν τη στιγμή."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Αυτή η εφαρμογή δημιουργήθηκε για παλαιότερη έκδοση του Android και μπορεί να μην λειτουργεί σωστά. Δοκιμάστε να ελέγξετε εάν υπάρχουν ενημερώσεις ή επικοινωνήστε με τον προγραμματιστή."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Έλεγχος για ενημέρωση"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Έχετε νέα μηνύματα"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 15a059f..d15fc5a 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Turn on work profile?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Your work apps, notifications, data and other work profile features will be turned on"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Turn on"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"App is not available"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not available right now."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"This app was built for an older version of Android and may not work properly. Try checking for updates or contact the developer."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Check for update"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"You have new messages"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index f3ab25a..bec2e34 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Turn on work profile?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Your work apps, notifications, data and other work profile features will be turned on"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Turn on"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"App is not available"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not available right now."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"This app was built for an older version of Android and may not work properly. Try checking for updates or contact the developer."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Check for update"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"You have new messages"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 15a059f..d15fc5a 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Turn on work profile?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Your work apps, notifications, data and other work profile features will be turned on"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Turn on"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"App is not available"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not available right now."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"This app was built for an older version of Android and may not work properly. Try checking for updates or contact the developer."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Check for update"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"You have new messages"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 15a059f..d15fc5a 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Turn on work profile?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Your work apps, notifications, data and other work profile features will be turned on"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Turn on"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"App is not available"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not available right now."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"This app was built for an older version of Android and may not work properly. Try checking for updates or contact the developer."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Check for update"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"You have new messages"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index 871e528..42ec932 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‏‏‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‎‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‎‏‏‏‎‎‎‎‎Turn on work profile?‎‏‎‎‏‎"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‎‎‏‏‏‎‏‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‎Your work apps, notifications, data, and other work profile features will be turned on‎‏‎‎‏‎"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‎‎‎‎‎‏‎‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‏‏‎‏‎‏‏‎‏‎Turn on‎‏‎‎‏‎"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎App is not available‎‏‎‎‏‎"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ is not available right now.‎‏‎‎‏‎"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer.‎‏‎‎‏‎"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‏‎‎‎‏‎‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‏‎‎‏‎‎‎‏‎‎‏‏‎‎‏‏‏‏‏‎‏‎‎‏‏‎‏‎Check for update‎‏‎‎‏‎"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‎‏‏‎You have new messages‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 1cb9fde..ac555d5 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"¿Activar el perfil de trabajo?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Se activaran las apps de trabajo, los datos, las notificaciones y otras funciones del perfil de trabajo"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Activar"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"La app no está disponible"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> no está disponible en este momento."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Esta app se creó para una versión anterior de Android y es posible que no funcione correctamente. Busca actualizaciones o comunícate con el programador."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Buscar actualización"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Tienes mensajes nuevos"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index d8509a3..190664b 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -802,7 +802,7 @@
     <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"Introduce el código PIN para desbloquear."</string>
     <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"Código PIN incorrecto"</string>
     <string name="keyguard_label_text" msgid="3841953694564168384">"Para desbloquear el teléfono, pulsa la tecla de menú y, a continuación, pulsa 0."</string>
-    <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"Número de emergencia"</string>
+    <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"Llamada de emergencia"</string>
     <string name="lockscreen_carrier_default" msgid="6192313772955399160">"Sin servicio"</string>
     <string name="lockscreen_screen_locked" msgid="7364905540516041817">"Pantalla bloqueada"</string>
     <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Pulsa la tecla de menú para desbloquear el teléfono o realizar una llamada de emergencia."</string>
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"¿Activar el perfil de trabajo?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Tus aplicaciones, notificaciones, datos y otras funciones del perfil de trabajo se activarán"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Activar"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"La aplicación no está disponible"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"En estos momentos, <xliff:g id="APP_NAME">%1$s</xliff:g> no está disponible."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Esta aplicación se ha diseñado para una versión anterior de Android y es posible que no funcione correctamente. Busca actualizaciones o ponte en contacto con el desarrollador."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Buscar actualizaciones"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Tienes mensajes nuevos"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 2148c09..0c302ee 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Kas lülitada tööprofiil sisse?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Teie töörakendused, märguanded, andmed ja muud tööprofiili funktsioonid lülitatakse sisse"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Lülita sisse"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Rakendus ei ole saadaval"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei ole praegu saadaval."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"See rakendus on loodud Androidi vanema versiooni jaoks ega pruugi õigesti töötada. Otsige värskendusi või võtke ühendust arendajaga."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Otsi värskendust"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Teile on uusi sõnumeid"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 07bde5e..e7aedd5 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -517,7 +517,7 @@
     <string name="permdesc_imagesWrite" msgid="5195054463269193317">"Argazki-bilduma aldatzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_mediaLocation" msgid="7368098373378598066">"multimedia-edukien bildumako kokapena irakurri"</string>
     <string name="permdesc_mediaLocation" msgid="597912899423578138">"Multimedia-edukien bildumako kokapena irakurtzeko baimena ematen die aplikazioei."</string>
-    <string name="biometric_dialog_default_title" msgid="5284880398508155088">"Egiaztatu zu zarela"</string>
+    <string name="biometric_dialog_default_title" msgid="5284880398508155088">"Egiaztatu zeu zarela"</string>
     <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometrikoa ez dago erabilgarri"</string>
     <string name="biometric_error_user_canceled" msgid="6732303949695293730">"Utzi da autentifikazioa"</string>
     <string name="biometric_not_recognized" msgid="5106687642694635888">"Ez da ezagutu"</string>
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Laneko profila aktibatu?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Laneko aplikazioak, jakinarazpenak, datuak eta laneko profileko bestelako eginbideak aktibatuko dira"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Aktibatu"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Aplikazioa ez dago erabilgarri"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ez dago erabilgarri une honetan."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Aplikazioa Android-en bertsio zaharrago baterako sortu zenez, baliteke behar bezala ez funtzionatzea. Bilatu eguneratzerik baden, edo jarri garatzailearekin harremanetan."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Bilatu eguneratzeak"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Mezu berriak dituzu"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index d65357b..9d9a13c 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"نمایه کاری روشن شود؟"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"برنامه‌ها، اعلان‌ها، داده‌ها و سایر قابلیت‌های نمایه کاری شما روشن خواهد شد"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"روشن کردن"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"برنامه در دسترس نیست"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> درحال‌حاضر در دسترس نیست."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‏این برنامه برای نسخه قدیمی‌تری از Android ساخته شده است و ممکن است درست کار نکند. وجود به‌روزرسانی را بررسی کنید یا با برنامه‌نویس تماس بگیرید."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"بررسی وجود به‌روزرسانی"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"پیام‌های جدیدی دارید"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 17b3d33b..35cc8b9 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Otetaanko työprofiili käyttöön?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Työsovellukset, ‑ilmoitukset, ‑tiedot ja muut työprofiiliominaisuudet otetaan käyttöön"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Ota käyttöön"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Sovellus ei ole käytettävissä"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei ole nyt käytettävissä."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Tämä sovellus on suunniteltu vanhemmalle Android-versiolle eikä välttämättä toimi oikein. Kokeile tarkistaa päivitykset tai ottaa yhteyttä kehittäjään."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Tarkista päivitykset"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Sinulle on uusia viestejä"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 45b8613..caec9fa 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Activer le profil professionnel?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Vos applications professionnelles, vos notifications, vos données et les autres fonctionnalités de profil professionnel seront activées"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Activer"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"L\'application n\'est pas accessible"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas accessible pour le moment."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Cette application a été conçue pour une ancienne version d\'Android et pourrait ne pas fonctionner correctement. Essayez de vérifier les mises à jour ou communiquez avec son concepteur."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Vérifier la présence de mises à jour"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Vous avez de nouveaux messages"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index c646aeb..601fe43 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Activer profil professionnel ?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Vos applications professionnelles, notifications, données et d\'autres fonctionnalités de votre profil professionnel seront activées"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Activer"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Application non disponible"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas disponible pour le moment."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Cette application a été conçue pour une ancienne version d\'Android et risque de ne pas fonctionner correctement. Recherchez des mises à jour ou contactez le développeur."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Rechercher une mise à jour"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Vous avez de nouveaux messages"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 2a2d7c4..0aa6277 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Activar o perfil de traballo?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Activaranse as túas aplicacións de traballo, as notificacións, os datos e outras funcións do perfil de traballo"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Activar"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"A aplicación non está dispoñible"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"A aplicación <xliff:g id="APP_NAME">%1$s</xliff:g> non está dispoñible neste momento."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Esta aplicación deseñouse para unha versión anterior de Android e quizais non funcione correctamente. Proba a buscar actualizacións ou contacta co programador."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Buscar actualización"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Tes mensaxes novas"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index aab09f7..85bf30e 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"કાર્યાલયની પ્રોફાઇલ ચાલુ કરીએ?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"તમારી કાર્યાલયની ઍપ, નોટિફિકેશન, ડેટા અને અન્ય કાર્યાલયની પ્રોફાઇલ સુવિધાઓ ચાલુ કરવામાં આવશે"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"ચાલુ કરો"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"ઍપ ઉપલબ્ધ નથી"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> હાલમાં ઉપલબ્ધ નથી."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"આ ઍપ Androidના જૂના વર્ઝન માટે બનાવવામાં આવ્યું હતું અને તે કદાચ તે યોગ્ય રીતે કાર્ય કરી શકશે નહીં. અપડેટ માટે તપાસવાનો પ્રયાસ કરો અથવા ડેવલપરનો સંપર્ક કરો."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"અપડેટ માટે તપાસો"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"તમારી પાસે નવા સંદેશા છે"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index e1bb060..19e154e 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"वर्क प्रोफ़ाइल चालू करें?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"आपके काम से जुड़े ऐप्लिकेशन, सूचनाएं, डेटा और वर्क प्रोफ़ाइल से जुड़ी दूसरी सुविधाएं चालू हो जाएंगी"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"चालू करें"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"ऐप्लिकेशन उपलब्ध नहीं है"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> इस समय उपलब्ध नहीं है."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"यह ऐप्लिकेशन Android के पुराने वर्शन के लिए बनाया गया था, इसलिए हो सकता है कि यह सही से काम न करे. देखें कि अपडेट मौजूद हैं या नहीं, या फिर डेवलपर से संपर्क करें."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"देखें कि अपडेट मौजूद है या नहीं"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"आपके पास नए संदेश हैं"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 3839434..9a9357f 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1885,6 +1885,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Želite uključiti radni profil?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Uključit će se vaše radne aplikacije, obavijesti, podaci i druge značajke radnog profila"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Uključi"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija nije dostupna"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutačno nije dostupna."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ova je aplikacija razvijena za stariju verziju Androida i možda neće funkcionirati pravilno. Potražite ažuriranja ili se obratite razvojnom programeru."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Provjeri ažuriranja"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Imate nove poruke"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index fcea1ec..d4ac7e2 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Bekapcsolja a munkaprofilt?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"A munkahelyi alkalmazások, értesítések, adatok és a munkaprofilhoz tartozó egyéb funkciók be lesznek kapcsolva"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Bekapcsolás"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Az alkalmazás nem hozzáférhető"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> jelenleg nem hozzáférhető."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ez az alkalmazás az Android egyik korábbi verziójához készült, így elképzelhető, hogy nem működik majd megfelelően ezen a rendszeren. Keressen frissítéseket, vagy vegye fel a kapcsolatot a fejlesztővel."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Frissítés keresése"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Új üzenetei érkeztek"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index cbd754f..5cd8e8e 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Միացնե՞լ աշխատանքային պրոֆիլը"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Ձեր աշխատանքային հավելվածները, ծանուցումները, տվյալները և աշխատանքային պրոֆիլի մյուս գործառույթները կմիանան"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Միացնել"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Հավելվածը հասանելի չէ"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածն այս պահին հասանելի չէ։"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Այս հավելվածը ստեղծվել է Android-ի ավելի հին տարբերակի համար և կարող է պատշաճ չաշխատել: Ստուգեք թարմացումների առկայությունը կամ դիմեք մշակողին:"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Ստուգել նոր տարբերակի առկայությունը"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Դուք ունեք նոր հաղորդագրություններ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f1a94ff..3a1571d 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Aktifkan profil kerja?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Aplikasi kerja, notifikasi, data, dan fitur profil kerja lainnya akan diaktifkan"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Aktifkan"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Aplikasi tidak tersedia"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak tersedia saat ini."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Aplikasi ini dibuat untuk Android versi lama dan mungkin tidak berfungsi sebagaimana mestinya. Coba periksa apakah ada update, atau hubungi developer."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Periksa apakah ada update"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Ada pesan baru"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index b4cb76d3..eddb214 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Kveikja á vinnusniði?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Kveikt verður á vinnuforritum, tilkynningum, gögnum og öðrum eiginleikum vinnusniðsins"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Kveikja"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Forrit er ekki tiltækt"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ekki tiltækt núna."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Þetta forrit var hannað fyrir eldri útgáfu af Android og ekki er víst að það virki eðlilega. Athugaðu hvort uppfærslur séu í boði eða hafðu samband við þróunaraðilann."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Leita að uppfærslu"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Þú ert með ný skilaboð"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 23a62a8..135a226 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Attivare il profilo di lavoro?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Le tue app di lavoro, le notifiche, i dati e altri elementi del profilo di lavoro saranno attivati."</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Attiva"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"L\'app non è disponibile"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> non è al momento disponibile."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Questa app è stata realizzata per una versione precedente di Android e potrebbe non funzionare correttamente. Prova a verificare la disponibilità di aggiornamenti o contatta lo sviluppatore."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Verifica la presenza di aggiornamenti"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Hai nuovi messaggi"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 4319aa8..65b7384 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1917,6 +1917,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"להפעיל את פרופיל העבודה?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"אפליקציות העבודה, התראות, נתונים ותכונות נוספות של פרופיל העבודה יופעלו"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"הפעל"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"האפליקציה לא זמינה"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> לא זמינה בשלב זה."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‏האפליקציה הזו עוצבה לגרסה ישנה יותר של Android וייתכן שלא תפעל כראוי. ניתן לבדוק אם יש עדכונים או ליצור קשר עם המפתח."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"האם יש עדכון חדש?"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"יש לך הודעות חדשות"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 481af76..bda2064 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"仕事用プロファイルの有効化"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"仕事用のアプリ、通知、データなど、仕事用プロファイルの機能が ON になります"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"ON にする"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"アプリの利用不可"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"現在 <xliff:g id="APP_NAME">%1$s</xliff:g> はご利用になれません。"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"このアプリは以前のバージョンの Android 用に作成されており、正常に動作しない可能性があります。アップデートを確認するか、デベロッパーにお問い合わせください。"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"アップデートを確認"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"新着メッセージがあります"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index c2c28e5..c47b710 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"ჩაირთოს სამსახურის პროფილი?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"თქვენი სამსახურის აპები, შეტყობინებები, მონაცემები და სამსახურის პროფილის ყველა სხვა ფუნქცია ჩაირთვება"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"ჩართვა"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"აპი მიუწვდომელია"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ამჟამად მიუწვდომელია."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ეს აპი Android-ის ძველი ვერსიისთვის შეიქმნა და შესაძლოა სათანადოდ არ მუშაობდეს. გადაამოწმეთ განახლებები ან დაუკავშირდით დეველოპერს."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"განახლების შემოწმება"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"თქვენ ახალი შეტყობინებები გაქვთ"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 7cb0512..241cf08 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Жұмыс профилі қосылсын ба?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Жұмыс қолданбалары, хабарландырулар, деректер және басқа да жұмыс профильдерінің мүмкіндіктері қосылады"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Қосу"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Қолданба қолжетімді емес"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> қазір қолжетімді емес."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Қолданба Android жүйесінің ескі нұсқасына арналған және дұрыс жұмыс істемеуі мүмкін. Жаңартылған нұсқаны тексеріңіз немесе әзірлеушіге хабарласыңыз."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Жаңартылған нұсқаны тексеру"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Сізде жаңа хабарлар бар"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index e0267d3..483e236 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -1855,6 +1855,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"បើក​កម្រង​ព័ត៌មាន​ការ​ងារ?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"កម្មវិធី​ការងារ ការ​ជូនដំណឹង ទិន្នន័យ និង​មុខងារ​កម្រង​ព័ត៌មាន​ការងារ​ផ្សេង​ទៀត​របស់អ្នក​នឹង​ត្រូវ​បាន​បើក"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"បើក"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"មិនអាច​ប្រើ​កម្មវិធី​នេះបានទេ"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"មិនអាច​ប្រើ <xliff:g id="APP_NAME">%1$s</xliff:g> នៅពេល​នេះ​បានទេ​។"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"កម្មវិធី​នេះ​ត្រូវបាន​បង្កើត​ឡើង​សម្រាប់​កំណែ​ប្រព័ន្ធ​ប្រតិបត្តិការ Android ចាស់ ហើយ​វាអាច​ដំណើរការ​ខុសប្រក្រតី។ សូម​សាកល្បង​ពិនិត្យមើល​កំណែ​ថ្មី ឬ​ទាក់ទង​ទៅអ្នក​អភិវឌ្ឍន៍។"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"រក​មើល​កំណែ​ថ្មី"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"អ្នកមានសារថ្មី"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 4a2bc72..7133e89 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ಆನ್ ಮಾಡುವುದೇ?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"ನಿಮ್ಮ ಕೆಲಸದ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು, ಅಧಿಸೂಚನೆಗಳು, ಡೇಟಾ ಮತ್ತು ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ನ ಇತರ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಆನ್ ಮಾಡಲಾಗುತ್ತದೆ"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"ಆನ್‌ ಮಾಡಿ"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"ಆ್ಯಪ್ ಲಭ್ಯವಿಲ್ಲ"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಇದೀಗ ಲಭ್ಯವಿಲ್ಲ."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು Android ನ ಹಳೆಯ ಆವೃತ್ತಿಗೆ ರಚಿಸಲಾಗಿದೆ ಮತ್ತು ಸರಿಯಾಗಿ ಕೆಲಸ ಮಾಡದಿರಬಹುದು. ಅಪ್‌ಡೇಟ್‌ಗಳನ್ನು ಪರಿಶೀಲಿಸಲು ಪ್ರಯತ್ನಿಸಿ ಅಥವಾ ಡೆವಲಪರ್ ಅನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ಅಪ್‌ಡೇಟ್‌ಗಾಗಿ ಪರಿಶೀಲಿಸಿ"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"ನೀವು ಹೊಸ ಸಂದೇಶಗಳನ್ನು ಹೊಂದಿರುವಿರಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 0ac7a14..12c05ba 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"직장 프로필을 사용 설정하시겠어요?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"업무용 앱, 알림, 데이터 및 기타 직장 프로필 기능이 사용 설정됩니다."</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"사용 설정"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"앱을 사용할 수 없습니다"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"현재 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱을 사용할 수 없습니다."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"이 앱은 Android 이전 버전에 맞게 개발되었기 때문에 제대로 작동하지 않을 수 있습니다. 업데이트를 확인하거나 개발자에게 문의하세요."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"업데이트 확인"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"새 메시지 있음"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 88e36ff..ccbf845 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -190,7 +190,7 @@
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Өтө көп жолу сырсөздү киргизүү аракети жасалды"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Админ түзмөктөн жеке колдонуу үчүн баш тартты"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Түзмөктү ишкана башкарат"</string>
-    <string name="network_logging_notification_text" msgid="1327373071132562512">"Ишканаңыз бул түзмөктү башкарат жана тармак трафигин көзөмөлдөшү мүмкүн. Чоо-жайын көрүү үчүн таптап коюңуз."</string>
+    <string name="network_logging_notification_text" msgid="1327373071132562512">"Ишканаңыз бул түзмөктү башкарат жана тармак трафигин көзөмөлдөшү мүмкүн. Чоо-жайын билгиңиз келсе, таптап коюңуз."</string>
     <string name="factory_reset_warning" msgid="6858705527798047809">"Түзмөгүңүз тазаланат"</string>
     <string name="factory_reset_message" msgid="2657049595153992213">"Түзмөктү башкаруучу колдонмо жараксыз. Түзмөгүңүз азыр тазаланат.\n\nСуроолоруңуз болсо, ишканаңыздын администраторуна кайрылыңыз."</string>
     <string name="printing_disabled_by" msgid="3517499806528864633">"Басып чыгаруу <xliff:g id="OWNER_APP">%s</xliff:g> тарабынан өчүрүлдү."</string>
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Жумуш профили күйгүзүлсүнбү?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Жумуш колдонмолоруңуз, эскертмелериңиз, дайын-даректериңиз жана жумуш профилинин башка функциялары күйгүзүлөт."</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Күйгүзүү"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Колдонмо учурда жеткиликсиз"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> учурда жеткиликсиз"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Бул колдонмо Android\'дин эски версиясы үчүн иштеп чыгарылган, андыктан туура эмес иштеши мүмкүн. Жаңыртууларды издеп көрүңүз же иштеп чыгуучуга кайрылыңыз."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Жаңыртууну издөө"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Сизге жаңы билдирүүлөр келди"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 63b87ac..e8bda53 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"ເປີດໃຊ້ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກບໍ?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"ແອັບວຽກຂອງທ່ານ, ການແຈ້ງເຕືອນ, ຂໍ້ມູນ ແລະ ຄຸນສົມບັດໂປຣໄຟລ໌ວຽກຈະຖືກເປີດໃຊ້"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"ເປີດ​"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"ແອັບບໍ່ສາມາດໃຊ້ໄດ້"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ່ສາມາດໃຊ້ໄດ້ໃນຕອນນີ້."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ແອັບນີ້ຖືກສ້າງຂຶ້ນສຳລັບ Android ເວີຊັນທີ່ເກົ່າກວ່າ ແລະ ອາດເຮັດວຽກໄດ້ບໍ່ປົກກະຕິ. ໃຫ້ລອງກວດສອບເບິ່ງອັບເດດ ຫຼື ຕິດຕໍ່ຜູ້ພັດທະນາ."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ກວດເບິ່ງອັບເດດ"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"ທ່ານມີຂໍ້ຄວາມໃໝ່"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index b8d848e..ad90ab3 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1917,6 +1917,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Įjungti darbo profilį?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Darbo programos, pranešimai, duomenys ir kitos darbo profilio funkcijos bus išjungtos"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Įjungti"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Programa nepasiekiama."</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ šiuo metu nepasiekiama."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ši programa sukurta naudoti senesnės versijos sistemoje „Android“ ir gali tinkamai neveikti. Pabandykite patikrinti, ar yra naujinių, arba susisiekite su kūrėju."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Tikrinti, ar yra naujinių"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Turite naujų pranešimų"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index f168bb4..6ffe39a 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1885,6 +1885,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Vai ieslēgt darba profilu?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Tiks ieslēgtas jūsu darba lietotnes, paziņojumi, dati un citas darba profila funkcijas."</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Ieslēgt"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Lietotne nav pieejama"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pašlaik nav pieejama."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Šī lietotne tika izstrādāta vecākai Android versijai un var nedarboties pareizi. Meklējiet atjauninājumus vai sazinieties ar izstrādātāju."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Meklēt atjauninājumu"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Jums ir jaunas īsziņas."</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index f94d191..bb1d9c6 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1855,6 +1855,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Да се вклучи работниот профил?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Вашите работни апликации, известувања, податоци и други функции на работниот профил ќе бидат вклучени"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Вклучи"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Апликацијата не е достапна"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> не е достапна во моментов."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Апликацијава е создадена за постара верзија на Android и може да не функционира правилно. Проверете за ажурирања или контактирајте со програмерот."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Проверка за ажурирање"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Имате нови пораки"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 1efd211..31edd8c 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"ഔദ്യോഗിക പ്രൊഫൈൽ ഓണാക്കണോ?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"നിങ്ങളുടെ ഔദ്യോഗിക ആപ്പുകൾ, അറിയിപ്പുകൾ, ഡാറ്റ, മറ്റ് ഔദ്യോഗിക പ്രൊഫൈൽ ഫീച്ചറുകൾ എന്നിവ ഓണാക്കും"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"ഓണാക്കുക"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"ആപ്പ് ലഭ്യമല്ല"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ഇപ്പോൾ ലഭ്യമല്ല."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ഈ ആപ്പ് Android-ന്റെ പഴയ പതിപ്പിനായി നിർമ്മിച്ചിരിക്കുന്നതിനാൽ ശരിയായി പ്രവർത്തിച്ചേക്കില്ല. അപ്‌ഡേറ്റിനായി പരിശോധിക്കുക, അല്ലെങ്കിൽ ഡെവലപ്പറുമായി ബന്ധപ്പെടുക."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"അപ്‌ഡേറ്റിനായി പരിശോധിക്കുക"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"നിങ്ങൾക്ക് പുതിയ സന്ദേശങ്ങൾ ഉണ്ട്"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index cc03175..eb61a46 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Ажлын профайлыг асаах уу?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Таны ажлын апп, мэдэгдэл, өгөгдөл болон бусад ажлын профайлын онцлогийг асаана"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Асаах"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Апп боломжгүй байна"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> яг одоо боломжгүй байна."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Энэ аппыг Андройдын хуучин хувилбарт зориулсан бөгөөд буруу ажиллаж болзошгүй. Шинэчлэлтийг шалгаж эсвэл хөгжүүлэгчтэй холбогдоно уу."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Шинэчлэлтийг шалгах"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Танд шинэ зурвасууд байна"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index cd9b6fc..0a8f4af 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"कार्य प्रोफाइल चालू ठेवायची?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"तुमची कार्य अ‍ॅप्स, सूचना, डेटा आणि अन्य कार्य प्रोफाइल वैशिष्ट्ये चालू केली जातील"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"चालू करा"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"ॲप उपलब्ध नाही"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> आता उपलब्ध नाही."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"हे अ‍ॅप Android च्या जुन्या आवृत्ती साठी तयार करण्यात आले होते आणि योग्यरितीने कार्य करू शकणार नाही. अपडेट आहेत का ते तपासून पाहा, किंवा डेव्हलपरशी संपर्क साधण्याचा प्रयत्न करा."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"अपडेट आहे का ते तपासा"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"आपल्याकडे नवीन मेसेज आहेत"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 0a40735..59c7d65 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Hidupkan profil kerja?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Apl kerja, pemberitahuan, data dan ciri profil kerja anda yang lain akan dihidupkan"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Hidupkan"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Apl tidak tersedia"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak tersedia sekarang."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Apl ini dibina untuk versi Android yang lebih lama dan mungkin tidak berfungsi dengan betul. Cuba semak kemas kini atau hubungi pembangun."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Semak kemas kini"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Anda mempunyai mesej baharu"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 9e04841..b3efcb1 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"အလုပ်ပရိုဖိုင် ဖွင့်လိုသလား။"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"သင်၏ အလုပ်အက်ပ်၊ အကြောင်းကြားချက်၊ ဒေတာနှင့် အခြားအလုပ်ပရိုဖိုင် ဝန်ဆောင်မှုများကို ဖွင့်လိုက်ပါမည်"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"ဖွင့်ပါ"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"အက်ပ်ကို မရနိုင်ပါ"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ယခု မရနိုင်ပါ။"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ဤအက်ပ်ကို Android ဗားရှင်းဟောင်းအတွက် ပြုလုပ်ထားခြင်းဖြစ်ပြီး ပုံမှန်အလုပ်မလုပ်နိုင်ပါ။ အပ်ဒိတ်များအတွက် ရှာကြည့်ပါ သို့မဟုတ် ဆော့ဖ်ဝဲအင်ဂျင်နီယာကို ဆက်သွယ်ပါ။"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"အပ်ဒိတ်စစ်ရန်"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"သင့်ထံတွင် စာအသစ်များရောက်နေသည်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 45ab3e5..5a3a588 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Vil du slå på jobbprofilen?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Jobbappene dine samt varsler, data og andre funksjoner i jobbprofilen din blir slått på"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Slå på"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Appen er ikke tilgjengelig"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> er ikke tilgjengelig for øyeblikket."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Denne appen er utviklet for en eldre versjon av Android og fungerer kanskje ikke som den skal. Prøv å se etter oppdateringer, eller kontakt utvikleren."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Se etter oppdateringer"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Du har nye meldinger"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 11033a7..e95866b 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -1859,6 +1859,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"कार्य प्रोफाइल सक्रिय गर्ने?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"तपाईंका कार्यसम्बन्धी अनुप्रयोग, सूचना, डेटा र कार्य प्रोफाइलका अन्य सुविधाहरू सक्रिय गरिने छन्‌"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"सक्रिय गर्नुहोस्"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"अनुप्रयोग उपलब्ध छैन"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> अहिले उपलब्ध छैन।"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"यो अनुप्रयोग Android को पुरानो संस्करणका लागि बनाइएको हुनाले यसले सही ढङ्गले काम नगर्न सक्छ। अद्यावधिकहरू उपलब्ध छन् वा छैनन् भनी जाँच गरी हेर्नुहोस् वा यसको विकासकर्तालाई सम्पर्क गर्नुहोस्।"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"अद्यावधिक उपलब्ध छ वा छैन भनी जाँच गर्नुहोस्"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"तपाईंलाई नयाँ सन्देश आएको छ"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 13f6725..e524c1b 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Werkprofiel inschakelen?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Je werk-apps, meldingen, gegevens en andere functies van je werkprofiel worden uitgeschakeld"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Inschakelen"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"App is niet beschikbaar"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> is momenteel niet beschikbaar."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Deze app is ontwikkeld voor een oudere versie van Android en werkt mogelijk niet op de juiste manier. Controleer op updates of neem contact op met de ontwikkelaar."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Controleren op update"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Je hebt nieuwe berichten"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 4424af7..246b4d9 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"ୱର୍କ ପ୍ରୋଫାଇଲ୍‌କୁ ଚାଲୁ କରିବେ?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"ଆପଣଙ୍କର କାର୍ଯ୍ୟକାରୀ ଆପ୍‌, ବିଜ୍ଞପ୍ତି, ଡାଟା ଓ ଅନ୍ୟ ୱର୍କ ପ୍ରୋଫାଇଲ୍‌ଗୁଡ଼ିକ ଚାଲୁ ହୋଇଯିବ"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"ଅନ୍ କରନ୍ତୁ"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"ଆପ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବର୍ତ୍ତମାନ ଉପଲବ୍ଧ ନାହିଁ।"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ଏହି ଆପ୍‌କୁ Androidର ପୁରୁଣା ଭର୍ସନ୍ ପାଇଁ ନିର୍ମାଣ କରାଯାଇଥିଲା ଏବଂ ଠିକ୍ ଭାବେ କାମ କରିନପାରେ। ଏହାପାଇଁ ଅପଡେଟ୍‌ ଅଛି କି ନାହିଁ ଯାଞ୍ଚ କରନ୍ତୁ କିମ୍ବା ଡେଭେଲପର୍‌ଙ୍କ ସହିତ ସମ୍ପର୍କ କରନ୍ତୁ।"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ଅପଡେଟ୍‌ ପାଇଁ ଯାଞ୍ଚ କରନ୍ତୁ"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"ଆପଣଙ୍କ ପାଖରେ ନୂଆ ମେସେଜ୍‍ ରହିଛି"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 8c87581..3d68832 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"ਕੀ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਚਾਲੂ ਕਰਨੀ ਹੈ?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"ਤੁਹਾਡੀਆਂ ਕਾਰਜ-ਸਥਾਨ ਐਪਾਂ, ਸੂਚਨਾਵਾਂ, ਡਾਟਾ ਅਤੇ ਹੋਰ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਚਾਲੂ ਕੀਤੀਆਂ ਜਾਣਗੀਆਂ"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"ਚਾਲੂ ਕਰੋ"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"ਐਪ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਐਪ ਇਸ ਵੇਲੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ਇਹ ਐਪ Android ਦੇ ਕਿਸੇ ਵਧੇਰੇ ਪੁਰਾਣੇ ਵਰਜਨ ਲਈ ਬਣਾਈ ਗਈ ਸੀ ਅਤੇ ਸ਼ਾਇਦ ਸਹੀ ਢੰਗ ਨਾਲ ਕੰਮ ਨਾ ਕਰੇ। ਅੱਪਡੇਟਾਂ ਲਈ ਜਾਂਚ ਕਰੋ ਜਾਂ ਵਿਕਾਸਕਾਰ ਨਾਲ ਸੰਪਰਕ ਕਰੋ।"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ਅੱਪਡੇਟ ਦੀ ਜਾਂਚ ਕਰੋ"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"ਤੁਹਾਨੂੰ ਨਵੇਂ ਸੁਨੇਹੇ ਪ੍ਰਾਪਤ ਹੋਏ ਹਨ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 5efbfbe..2fb9ac2 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1917,6 +1917,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Włączyć profil służbowy?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Aplikacje do pracy, powiadomienia, dane i inne funkcje profilu do pracy zostaną włączone"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Włącz"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacja jest niedostępna"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest obecnie niedostępna."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ta aplikacja jest na starszą wersję Androida i może nie działać prawidłowo. Sprawdź dostępność aktualizacji lub skontaktuj się z programistą."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Sprawdź dostępność aktualizacji"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Masz nowe wiadomości"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index db48441..1ae0c36 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -509,8 +509,8 @@
     <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Permite que o app execute métodos para adicionar e excluir modelos de impressão digital para uso."</string>
     <string name="permlab_useFingerprint" msgid="1001421069766751922">"usar hardware de impressão digital"</string>
     <string name="permdesc_useFingerprint" msgid="412463055059323742">"Permite que o app use hardware de impressão digital para autenticação."</string>
-    <string name="permlab_audioWrite" msgid="8501705294265669405">"modificar sua coleção de músicas"</string>
-    <string name="permdesc_audioWrite" msgid="8057399517013412431">"Permite que o app modifique sua coleção de músicas."</string>
+    <string name="permlab_audioWrite" msgid="8501705294265669405">"modificar sua biblioteca de música"</string>
+    <string name="permdesc_audioWrite" msgid="8057399517013412431">"Permite que o app modifique sua biblioteca de música."</string>
     <string name="permlab_videoWrite" msgid="5940738769586451318">"modificar sua coleção de vídeos"</string>
     <string name="permdesc_videoWrite" msgid="6124731210613317051">"Permite que o app modifique sua coleção de vídeos."</string>
     <string name="permlab_imagesWrite" msgid="1774555086984985578">"modificar sua coleção de fotos"</string>
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Ativar o perfil de trabalho?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Seus apps, notificações, dados e outros recursos do perfil de trabalho serão ativados"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Ativar"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"O app não está disponível"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não está disponível no momento."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Este app foi criado para uma versão mais antiga do Android e pode não funcionar corretamente. Tente verificar se há atualizações ou entre em contato com o desenvolvedor."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Procurar atualizações"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Você tem mensagens novas"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 04296fe..c381709 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Ativar o perfil de trabalho?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"As aplicações de trabalho, as notificações, os dados e outras funcionalidades do perfil de trabalho serão desativados"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Ativar"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"A app não está disponível"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"De momento, a app <xliff:g id="APP_NAME">%1$s</xliff:g> não está disponível."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Esta aplicação foi concebida para uma versão mais antiga do Android e pode não funcionar corretamente. Experimente verificar se existem atualizações ou contacte o programador."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Verificar se existem atualizações"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Tem mensagens novas"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index db48441..1ae0c36 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -509,8 +509,8 @@
     <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"Permite que o app execute métodos para adicionar e excluir modelos de impressão digital para uso."</string>
     <string name="permlab_useFingerprint" msgid="1001421069766751922">"usar hardware de impressão digital"</string>
     <string name="permdesc_useFingerprint" msgid="412463055059323742">"Permite que o app use hardware de impressão digital para autenticação."</string>
-    <string name="permlab_audioWrite" msgid="8501705294265669405">"modificar sua coleção de músicas"</string>
-    <string name="permdesc_audioWrite" msgid="8057399517013412431">"Permite que o app modifique sua coleção de músicas."</string>
+    <string name="permlab_audioWrite" msgid="8501705294265669405">"modificar sua biblioteca de música"</string>
+    <string name="permdesc_audioWrite" msgid="8057399517013412431">"Permite que o app modifique sua biblioteca de música."</string>
     <string name="permlab_videoWrite" msgid="5940738769586451318">"modificar sua coleção de vídeos"</string>
     <string name="permdesc_videoWrite" msgid="6124731210613317051">"Permite que o app modifique sua coleção de vídeos."</string>
     <string name="permlab_imagesWrite" msgid="1774555086984985578">"modificar sua coleção de fotos"</string>
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Ativar o perfil de trabalho?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Seus apps, notificações, dados e outros recursos do perfil de trabalho serão ativados"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Ativar"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"O app não está disponível"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não está disponível no momento."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Este app foi criado para uma versão mais antiga do Android e pode não funcionar corretamente. Tente verificar se há atualizações ou entre em contato com o desenvolvedor."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Procurar atualizações"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Você tem mensagens novas"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 030e76a2..7c5b63e 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -1885,6 +1885,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Activați profilul de serviciu?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Se vor activa aplicațiile dvs. de serviciu, notificările, datele și alte funcții ale profilului de serviciu"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Activați"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Aplicația nu este disponibilă"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu este disponibilă momentan."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Această aplicație a fost creată pentru o versiune Android mai veche și este posibil să nu funcționeze corect. Încercați să căutați actualizări sau contactați dezvoltatorul."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Căutați actualizări"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Aveți mesaje noi"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 94029f5..fa2c9f5 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1177,7 +1177,7 @@
     <string name="whichEditApplication" msgid="6191568491456092812">"Редактировать с помощью приложения:"</string>
     <string name="whichEditApplicationNamed" msgid="8096494987978521514">"Редактировать с помощью приложения \"%1$s\""</string>
     <string name="whichEditApplicationLabel" msgid="1463288652070140285">"Изменить"</string>
-    <string name="whichSendApplication" msgid="4143847974460792029">"Отправка данных"</string>
+    <string name="whichSendApplication" msgid="4143847974460792029">"Поделиться"</string>
     <string name="whichSendApplicationNamed" msgid="4470386782693183461">"Поделиться через %1$s"</string>
     <string name="whichSendApplicationLabel" msgid="7467813004769188515">"Поделиться"</string>
     <string name="whichSendToApplication" msgid="77101541959464018">"Выберите приложение"</string>
@@ -1917,6 +1917,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Включить рабочий профиль?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Будут включены корпоративные приложения, уведомления, данные и другие функции рабочего профиля."</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Включить"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Приложение недоступно"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" сейчас недоступно."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Это приложение было создано для более ранней версии Android и может работать со сбоями. Проверьте наличие обновлений или свяжитесь с разработчиком."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Проверить обновления"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Новые сообщения"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 73cf018..dbe1f22 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -1855,6 +1855,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"කාර්යාල පැතිකඩ ක්‍රියාත්මක කරන්නද?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"ඔබගේ වැඩ යෙදුම්, දැනුම්දීම්, දත්ත සහ වෙනත් කාර්යාල පැතිකඩ විශේෂාංග ක්‍රියාත්මක කරනු ඇත"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"ක්‍රියාත්මක කරන්න"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"යෙදුම ලබා ගත නොහැකිය"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> මේ දැන් ලබා ගත නොහැකිය."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"මෙම යෙදුම Android හි පැරණි අනුවාදයක් සඳහා තනා ඇති අතර නිසියාකාරව ක්‍රියා නොකරනු ඇත. යාවත්කාලීන සඳහා පරික්ෂා කිරීම උත්සාහ කරන්න, නැතහොත් සංවර්ධක අමතන්න."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"යාවත්කාලීන සඳහා පරික්ෂා කරන්න"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"ඔබට නව පණිවිඩ තිබේ"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index b86fc46..5483efd 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1917,6 +1917,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Zapnúť pracovný profil?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Pracovné aplikácie, upozornenia, dáta a ďalšie funkcie pracovného profilu sa zapnú"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Zapnúť"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Aplikácia nie je dostupná"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> nie je teraz dostupná."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Táto aplikácia bola zostavená pre staršiu verziu Androidu a nemusí správne fungovať. Skúste skontrolovať dostupnosť aktualizácií alebo kontaktovať vývojára."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Skontrolovať dostupnosť aktualizácie"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Máte nové správy."</string>
@@ -1944,7 +1946,7 @@
     <string name="app_category_social" msgid="2278269325488344054">"Sociálne siete a komunikácia"</string>
     <string name="app_category_news" msgid="1172762719574964544">"Noviny a časopisy"</string>
     <string name="app_category_maps" msgid="6395725487922533156">"Mapy a navigácia"</string>
-    <string name="app_category_productivity" msgid="1844422703029557883">"Produktivita"</string>
+    <string name="app_category_productivity" msgid="1844422703029557883">"Kancelárske"</string>
     <string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Úložisko zariadenia"</string>
     <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Ladenie cez USB"</string>
     <string name="time_picker_hour_label" msgid="4208590187662336864">"hodina"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index bc2bc3a..fed39e8 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1917,6 +1917,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Želite vklopiti delovni profil?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Vklopili boste svoje delovne aplikacije, obvestila, podatke in druge funkcije delovnega profila"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Vklop"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacija ni na voljo"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> trenutno ni na voljo."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ta aplikacija je bila zasnovana za starejšo različico Androida in morda ne bo delovala pravilno. Preverite, ali so na voljo posodobitve, ali pa se obrnite na razvijalca."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Preveri, ali je na voljo posodobitev"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Imate nova sporočila."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index a353288..ec61fb9 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Të aktivizohet profili i punës?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Aplikacionet e punës, njoftimet, të dhënat e tua dhe funksionet e tjera të profilit të punës do të aktivizohen"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Aktivizo"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Aplikacioni nuk ofrohet"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> nuk ofrohet për momentin."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ky aplikacion është ndërtuar për një version më të vjetër të Android dhe mund të mos funksionojë mirë. Provo të kontrollosh për përditësime ose kontakto me zhvilluesin."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Kontrollo për përditësim"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Ke mesazhe të reja"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 8ee644a..e1c4e18 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -1885,6 +1885,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Да укључимо профил за Work?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Укључиће се пословне апликације, обавештења, подаци и друге функције профила за Work"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Укључи"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Апликација није доступна"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> тренутно није доступна."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ова апликација је направљена за старију верзију Android-а, па можда неће радити исправно. Потражите ажурирања или контактирајте програмера."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Потражи ажурирање"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Имате нове поруке"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 577933e6..0737b22 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Vill du aktivera jobbprofilen?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Jobbappar, aviseringar, data och andra funktioner i jobbprofilen aktiveras"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Aktivera"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Appen är inte tillgänglig"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> är inte tillgängligt just nu."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Appen har utvecklats för en äldre version av Android och kanske inte fungerar som den ska. Testa att söka efter uppdateringar eller kontakta utvecklaren."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Sök efter uppdateringar"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Du har nya meddelanden"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index a6ee366..3c6b0fa 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Ungependa kuwasha wasifu wa kazini?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Hatua hii itawasha data, arifa, programu za kazini, arifa na vipengele vingine vya wasifu wa kazini"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Washa"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Programu haipatikani"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> haipatikani hivi sasa."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Programu hii iliundwa kwa ajili ya toleo la zamani la Android na huenda isifanye kazi vizuri. Jaribu kuangalia masasisho au uwasiliane na msanidi programu."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Angalia masasisho"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Una ujumbe mpya"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index a56465f..e80f37b 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"பணிச் சுயவிவரத்தை ஆன் செய்யவா?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"பணி ஆப்ஸ், அறிவிப்புகள், தரவு மற்றும் பிற பணிச் சுயவிவர அம்சங்கள் ஆன் செய்யப்படும்"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"இயக்கு"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"இந்த ஆப்ஸ் இப்போது கிடைப்பதில்லை"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் இப்போது கிடைப்பதில்லை."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"இந்த ஆப்ஸ் Android இன் பழைய பதிப்புக்காக உருவாக்கப்பட்டதால், சரியாக வேலை செய்யாமல் போகலாம். புதுப்பிப்புகள் ஏதேனும் உள்ளதா எனப் பார்க்கவும் அல்லது டெவெலப்பரைத் தொடர்புகொள்ளவும்."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"புதுப்பிப்பு உள்ளதா எனப் பார்"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"புதிய செய்திகள் வந்துள்ளன"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 7a0aece..81d4b4f 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"కార్యాలయ ప్రొఫైల్‌ని ఆన్ చేయాలా?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"మీ కార్యాలయ యాప్‌లు, నోటిఫికేషన్‌లు, డేటా మరియు ఇతర కార్యాలయ ప్రొఫైల్ ఫీచర్‌లు ఆన్ చేయబడతాయి"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"ఆన్ చేయి"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"యాప్ అందుబాటులో లేదు"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రస్తుతం అందుబాటులో లేదు."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"ఈ యాప్ పాత వెర్షన్ Android కోసం రూపొందించబడింది మరియు అది సరిగ్గా పని చేయకపోవచ్చు. అప్‌డేట్‌ల కోసం తనిఖీ చేయడానికి ప్రయత్నించండి లేదా డెవలపర్‌ని సంప్రదించండి."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"అప్‌డేట్ కోసం తనిఖీ చేయండి"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"మీకు కొత్త సందేశాలు ఉన్నాయి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 98f2d88..46530ca 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"เปิดโปรไฟล์งานไหม"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"ระบบจะเปิดแอปงาน การแจ้งเตือน ข้อมูล และฟีเจอร์อื่นๆ ในโปรไฟล์งาน"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"เปิด"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"แอปไม่พร้อมใช้งาน"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ไม่พร้อมใช้งานในขณะนี้"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"แอปนี้สร้างขึ้นเพื่อ Android เวอร์ชันเก่าและอาจทำงานผิดปกติ โปรดลองตรวจหาการอัปเดตหรือติดต่อนักพัฒนาซอฟต์แวร์"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"ตรวจสอบอัปเดต"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"คุณมีข้อความใหม่"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index c38a6d5..b9f1f29 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"I-on ang profile sa trabaho?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Mao-on ang iyong mga app sa trabaho, notification, data, at iba pang feature sa profile sa trabaho"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"I-on"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Hindi available ang app"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Hindi available sa ngayon ang <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ang app na ito ay ginawa para sa mas lumang bersyon ng Android at maaaring hindi gumana nang maayos. Subukang tingnan kung may mga update, o makipag-ugnayan sa developer."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Tingnan kung may update"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Mayroon kang mga bagong mensahe"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 5e09309..81fb079 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"İş profili açılsın mı?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"İş uygulamalarınız, bildirimleriniz, verileriniz ve diğer iş profili özellikleriniz açılacak"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Aç"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Uygulama kullanılamıyor"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulaması şu anda kullanılamıyor."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Bu uygulama Android\'in daha eski bir sürümü için oluşturuldu ve düzgün çalışmayabilir. Güncellemeleri kontrol etmeyi deneyin veya geliştiriciyle iletişime geçin."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Güncellemeleri denetle"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Yeni mesajlarınız var"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 1096f97..c71ea74 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1917,6 +1917,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Увімкнути робочий профіль?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Додатки, сповіщення, дані й інші функції робочого профілю буде ввімкнено"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Увімкнути"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Додаток недоступний"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> зараз недоступний."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Цей додаток створений для старішої версії Android і може працювати неналежним чином. Спробуйте знайти оновлення або зв’яжіться з розробником."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Шукати оновлення"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"У вас є нові повідомлення"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 4c33191..8a0425b 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"دفتری پروفائل آن کریں؟"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"آپ کی دفتری ایپس، اطلاعات، ڈیٹا اور دفتری پروفائل کی دیگر خصوصیات آن کر دی جائیں گی"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"آن کریں"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"ایپ دستیاب نہیں ہے"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ابھی دستیاب نہیں ہے۔"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‏یہ ایپ Android کے پرانے ورژن کے لئے بنائی گئی ہے اور ہو سکتا ہے صحیح طور پر کام نہ کرے۔ اپ ڈیٹس چیک کر کے آزمائیں یا ڈیولپر سے رابطہ کریں۔"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"اپ ڈیٹ چیک کریں"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"آپ کے پاس نئے پیغامات ہیں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 23873b5..b5c31c2 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Ishchi profil yoqilsinmi?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Ishchi ilovalar, bildirishnomalar, ma’lumotlar va boshqa ishchi profil imkoniyatlari yoqiladi"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Yoqish"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Ilova ishlamayapti"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"Ayni vaqtda <xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi ishlamayapti."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Bu ilova eskiroq Android versiyalariga chiqarilgan va xato ishlashi mumkin. Yangilanishlarini tekshiring yoki dasturchi bilan bog‘laning."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Yangilanish borligini tekshirish"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Sizga yangi SMS keldi"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 1ed30d7..970d41f 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Bạn muốn bật hồ sơ công việc?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Ứng dụng công việc, thông báo, dữ liệu và các tính năng khác của hồ sơ công việc sẽ được bật"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Bật"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Ứng dụng này không dùng được"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> hiện không dùng được."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Ứng dụng này được xây dựng cho một phiên bản Android cũ hơn và có thể hoạt động không bình thường. Hãy thử kiểm tra các bản cập nhật hoặc liên hệ với nhà phát triển."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Kiểm tra bản cập nhật"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Bạn có tin nhắn mới"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 7671063..c9bba59 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"要开启工作资料吗?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"您的工作应用、通知、数据及其他工作资料功能将会开启"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"开启"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"应用无法使用"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g>目前无法使用。"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"此应用专为旧版 Android 打造,因此可能无法正常运行。请尝试检查更新或与开发者联系。"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"检查更新"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"您有新消息"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 89bea07..fc652e3 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"要開啟工作設定檔嗎?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"系統將開啟您的工作應用程式、通知、資料和其他工作設定檔功能"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"開啟"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"無法使用應用程式"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"目前無法使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"此應用程式專為舊版 Android 打造,因此可能無法正常運作。請嘗試檢查更新,或與開發人員聯絡。"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"檢查更新"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"您有新的訊息"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index c95ae37..75d05a2 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"要開啟工作資料夾嗎?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"系統將開啟你的辦公應用程式、通知、資料和其他工作資料夾功能"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"開啟"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"應用程式無法使用"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」目前無法使用。"</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"這個應用程式是專為舊版 Android 所打造,因此可能無法正常運作。請嘗試檢查更新,或是與開發人員聯絡。"</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"檢查更新"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"你有新訊息"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 0cd669b..0d2abc0 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1853,6 +1853,8 @@
     <string name="work_mode_off_title" msgid="5503291976647976560">"Vula iphrofayela yomsebenzi?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"Izinhlelo zakho zokusebenza zomsebenzi, izaziso, idatha, nezinye izici zephrofayela yomsebenzi kuzovulwa"</string>
     <string name="work_mode_turn_on" msgid="3662561662475962285">"Vula"</string>
+    <string name="app_blocked_title" msgid="7353262160455028160">"Uhlelo lokusebenza alutholakali"</string>
+    <string name="app_blocked_message" msgid="542972921087873023">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayitholakali khona manje."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Lolu hlelo lokusebenza belakhelwe inguqulo endala ye-Android futhi kungenzeka lungasebenzi kahle. Zama ukuhlolela izibuyekezo, noma uxhumane nonjiniyela."</string>
     <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Hlola izibuyekezo"</string>
     <string name="new_sms_notification_title" msgid="6528758221319927107">"Unemilayezo emisha"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 20901e0..7d8b8db 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3790,6 +3790,12 @@
         <attr name="description" />
         <!-- Brief summary of the target of accessibility shortcut purpose or behavior. -->
         <attr name="summary" />
+        <!-- Animated image of the target of accessibility shortcut purpose or behavior, to help
+             users understand how the target of accessibility shortcut can help them.-->
+        <attr name="animatedImageDrawable" format="reference"/>
+        <!-- Html description of the target of accessibility shortcut purpose or behavior, to help
+             users understand how the target of accessibility shortcut can help them. -->
+        <attr name="htmlDescription" format="string"/>
     </declare-styleable>
 
     <!-- Use <code>print-service</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b22e186..c66261b 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2056,6 +2056,9 @@
         <attr name="name" />
     </declare-styleable>
     <declare-styleable name="AndroidManifestQueriesIntent" parent="AndroidManifestQueries" />
+    <declare-styleable name="AndroidManifestQueriesProvider" parent="AndroidManifestQueries" >
+        <attr name="authorities" />
+    </declare-styleable>
 
 
     <!-- The <code>static-library</code> tag declares that this apk is providing itself
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 31e68e8..df63306 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2351,7 +2351,7 @@
          type. These are flags and can be freely combined.
          0  - disable whitelist (install all system packages; no logging)
          1  - enforce (only install system packages if they are whitelisted)
-         2  - log (log when a non-whitelisted package is run)
+         2  - log (log non-whitelisted packages)
          4  - any package not mentioned in the whitelist file is implicitly whitelisted on all users
          8  - same as 4, but just for the SYSTEM user
          16 - ignore OTAs (don't install system packages during OTAs)
@@ -3402,8 +3402,8 @@
     <!-- True if assistant app should be pinned via Pinner Service -->
     <bool name="config_pinnerAssistantApp">false</bool>
 
-    <!-- List of files pinned by the Pinner Service with the apex boot image b/119800099 -->
-    <string-array translatable="false" name="config_apexBootImagePinnerServiceFiles">
+    <!-- List of files pinned by the Pinner Service with the JIT Zygote boot image b/119800099 -->
+    <string-array translatable="false" name="config_jitzygoteBootImagePinnerServiceFiles">
     </string-array>
 
     <!-- Number of days preloaded file cache should be preserved on a device before it can be
@@ -3792,6 +3792,10 @@
          or empty if the default should be used. -->
     <string translatable="false" name="config_deviceSpecificAudioService"></string>
 
+    <!-- Class name of the device specific implementation of DisplayAreaPolicy.Provider
+         or empty if the default should be used. -->
+    <string translatable="false" name="config_deviceSpecificDisplayAreaPolicyProvider"></string>
+
     <!-- Component name of media projection permission dialog -->
     <string name="config_mediaProjectionPermissionDialogComponent" translatable="false">com.android.systemui/com.android.systemui.media.MediaProjectionPermissionActivity</string>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a54566c..08c8403 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -414,6 +414,14 @@
          logging. [CHAR LIMIT=NONE]-->
     <string name="network_logging_notification_text">Your organization manages this device and may monitor network traffic. Tap for details.</string>
 
+    <!-- Content title for a notification. This notification indicates that the device owner has
+         changed the location settings. [CHAR LIMIT=NONE] -->
+    <string name="location_changed_notification_title">Location settings changed by your admin</string>
+    <!-- Content text for a notification. Tapping opens device location settings.
+         [CHAR LIMIT=NONE] -->
+    <string name="location_changed_notification_text">Tap to see your location settings.</string>
+
+
     <!-- Factory reset warning dialog strings--> <skip />
     <!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
     <string name="factory_reset_warning">Your device will be erased</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2453bb1..0babe48 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1213,6 +1213,8 @@
   <java-symbol type="string" name="device_ownership_relinquished" />
   <java-symbol type="string" name="network_logging_notification_title" />
   <java-symbol type="string" name="network_logging_notification_text" />
+  <java-symbol type="string" name="location_changed_notification_title" />
+  <java-symbol type="string" name="location_changed_notification_text" />
   <java-symbol type="string" name="personal_apps_suspended_notification_title" />
   <java-symbol type="string" name="personal_apps_suspended_notification_text" />
   <java-symbol type="string" name="factory_reset_warning" />
@@ -3037,7 +3039,7 @@
   <java-symbol type="bool" name="config_pinnerCameraApp" />
   <java-symbol type="bool" name="config_pinnerHomeApp" />
   <java-symbol type="bool" name="config_pinnerAssistantApp" />
-  <java-symbol type="array" name="config_apexBootImagePinnerServiceFiles" />
+  <java-symbol type="array" name="config_jitzygoteBootImagePinnerServiceFiles" />
 
   <java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
 
@@ -3864,6 +3866,8 @@
   <!-- 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" />
 
+  <java-symbol type="string" name="config_deviceSpecificDisplayAreaPolicyProvider" />
+
   <!-- Whether to expand the lock screen user switcher by default -->
   <java-symbol type="bool" name="config_expandLockScreenUserSwitcher" />
 </resources>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 9f15faf..e04d3de 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -52,9 +52,14 @@
         "android.test.base",
         "android.test.mock",
         "framework-atb-backward-compatibility",
+        "framework-all",
+        "icing-java-proto-lite",
+        "ext",
+        "framework-res",
     ],
 
     platform_apis: true,
+    sdk_version: "core_platform",
     test_suites: ["device-tests"],
 
     certificate: "platform",
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 718ca46..59335a5 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1330,12 +1330,6 @@
             android:process=":FakeProvider">
         </provider>
 
-        <provider
-            android:name="android.content.SlowProvider"
-            android:authorities="android.content.SlowProvider"
-            android:process=":SlowProvider">
-        </provider>
-
         <!-- Application components used for os tests -->
 
         <service android:name="android.os.MessengerService"
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index f630188..21613a8 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -148,4 +148,7 @@
 
     <!-- Summary of the accessibility shortcut [CHAR LIMIT=NONE] -->
     <string name="accessibility_shortcut_summary">Accessibility shortcut summary</string>
+
+    <!-- Html description of the accessibility shortcut [CHAR LIMIT=NONE] -->
+    <string name="accessibility_shortcut_html_description">Accessibility shortcut html description</string>
 </resources>
diff --git a/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml b/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
index 60e2998..a597b71 100644
--- a/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
+++ b/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
@@ -19,4 +19,6 @@
 <accessibility-shortcut-target xmlns:android="http://schemas.android.com/apk/res/android"
                         android:description="@string/accessibility_shortcut_description"
                         android:summary="@string/accessibility_shortcut_summary"
+                        android:animatedImageDrawable="@drawable/bitmap_drawable"
+                        android:htmlDescription="@string/accessibility_shortcut_html_description"
 />
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
index ae6d8df..82a7b2c 100644
--- a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
@@ -84,6 +84,22 @@
     }
 
     @Test
+    public void testAnimatedImageRes() {
+        assertThat("Animated image resource id is not correct",
+                mShortcutInfo.getAnimatedImageRes(), is(R.drawable.bitmap_drawable));
+    }
+
+    @Test
+    public void testHtmlDescription() {
+        final String htmlDescription = mTargetContext.getResources()
+                .getString(R.string.accessibility_shortcut_html_description);
+
+        assertNotNull("Can't find html description string", htmlDescription);
+        assertThat("Html description is not correct",
+                mShortcutInfo.loadHtmlDescription(mPackageManager), is(htmlDescription));
+    }
+
+    @Test
     public void testEquals() {
         assertTrue(mShortcutInfo.equals(mShortcutInfo));
         assertFalse(mShortcutInfo.equals(null));
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 6dc7392..9dcce1e 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -209,13 +209,4 @@
         String type = mResolver.getType(Uri.parse("content://android.content.FakeProviderRemote"));
         assertEquals("fake/remote", type);
     }
-
-
-    @Test
-    public void testGetType_slowProvider() {
-        // This provider is running in a different process and is intentionally slow to start.
-        // We are trying to confirm that it does not cause an ANR
-        String type = mResolver.getType(Uri.parse("content://android.content.SlowProvider"));
-        assertEquals("slow", type);
-    }
 }
diff --git a/core/tests/coretests/src/android/content/SlowProvider.java b/core/tests/coretests/src/android/content/SlowProvider.java
deleted file mode 100644
index aba32e8..0000000
--- a/core/tests/coretests/src/android/content/SlowProvider.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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;
-
-import android.database.Cursor;
-import android.net.Uri;
-
-/**
- * A dummy content provider for tests.  This provider runs in a different process from the test and
- * is intentionally slow.
- */
-public class SlowProvider extends ContentProvider {
-
-    private static final int ON_CREATE_LATENCY_MILLIS = 3000;
-
-    @Override
-    public boolean onCreate() {
-        try {
-            Thread.sleep(ON_CREATE_LATENCY_MILLIS);
-        } catch (InterruptedException e) {
-            // Ignore
-        }
-        return true;
-    }
-
-    @Override
-    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
-            String sortOrder) {
-        return null;
-    }
-
-    @Override
-    public String getType(Uri uri) {
-        return "slow";
-    }
-
-    @Override
-    public Uri insert(Uri uri, ContentValues values) {
-        return null;
-    }
-
-    @Override
-    public int delete(Uri uri, String selection, String[] selectionArgs) {
-        return 0;
-    }
-
-    @Override
-    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
-        return 0;
-    }
-}
diff --git a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
index 3273e5d..ea69176 100644
--- a/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/AtomicFormulaTest.java
@@ -29,6 +29,9 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.util.Arrays;
+import java.util.Collections;
+
 @RunWith(JUnit4.class)
 public class AtomicFormulaTest {
 
@@ -230,7 +233,7 @@
     }
 
     @Test
-    public void testFormulaMatches_string_true() {
+    public void testFormulaMatches_string_packageNameFormula_true() {
         StringAtomicFormula stringAtomicFormula =
                 new StringAtomicFormula(
                         AtomicFormula.PACKAGE_NAME, "com.test.app", /* isHashedValue= */
@@ -242,7 +245,7 @@
     }
 
     @Test
-    public void testFormulaMatches_string_false() {
+    public void testFormulaMatches_string_packageNameFormula_false() {
         StringAtomicFormula stringAtomicFormula =
                 new StringAtomicFormula(
                         AtomicFormula.PACKAGE_NAME, "com.test.app", /* isHashedValue= */
@@ -253,6 +256,63 @@
         assertThat(stringAtomicFormula.matches(appInstallMetadata)).isFalse();
     }
 
+    @Test
+    public void testFormulaMatches_string_multipleAppCertificates_true() {
+        StringAtomicFormula stringAtomicFormula =
+                new StringAtomicFormula(
+                        AtomicFormula.APP_CERTIFICATE, "cert", /* isHashedValue= */ true);
+        AppInstallMetadata appInstallMetadata =
+                getAppInstallMetadataBuilder()
+                        .setPackageName("com.test.app")
+                        .setAppCertificates(Arrays.asList("test-cert", "cert"))
+                        .build();
+
+        assertThat(stringAtomicFormula.matches(appInstallMetadata)).isTrue();
+    }
+
+    @Test
+    public void testFormulaMatches_string_multipleAppCertificates_false() {
+        StringAtomicFormula stringAtomicFormula =
+                new StringAtomicFormula(
+                        AtomicFormula.APP_CERTIFICATE, "cert", /* isHashedValue= */ true);
+        AppInstallMetadata appInstallMetadata =
+                getAppInstallMetadataBuilder()
+                        .setPackageName("com.test.app")
+                        .setAppCertificates(Arrays.asList("test-cert", "another-cert"))
+                        .build();
+
+        assertThat(stringAtomicFormula.matches(appInstallMetadata)).isFalse();
+    }
+
+    @Test
+    public void testFormulaMatches_string_multipleInstallerCertificates_true() {
+        StringAtomicFormula stringAtomicFormula =
+                new StringAtomicFormula(
+                        AtomicFormula.INSTALLER_CERTIFICATE, "cert", /* isHashedValue= */ true);
+        AppInstallMetadata appInstallMetadata =
+                getAppInstallMetadataBuilder()
+                        .setPackageName("com.test.app")
+                        .setAppCertificates(Collections.singletonList("abc"))
+                        .setInstallerCertificates(Arrays.asList("test-cert", "cert"))
+                        .build();
+
+        assertThat(stringAtomicFormula.matches(appInstallMetadata)).isTrue();
+    }
+
+    @Test
+    public void testFormulaMatches_string_multipleInstallerCertificates_false() {
+        StringAtomicFormula stringAtomicFormula =
+                new StringAtomicFormula(
+                        AtomicFormula.INSTALLER_CERTIFICATE, "cert", /* isHashedValue= */ true);
+        AppInstallMetadata appInstallMetadata =
+                getAppInstallMetadataBuilder()
+                        .setPackageName("com.test.app")
+                        .setAppCertificates(Collections.singletonList("abc"))
+                        .setInstallerCertificates(Arrays.asList("test-cert", "another-cert"))
+                        .build();
+
+        assertThat(stringAtomicFormula.matches(appInstallMetadata)).isFalse();
+    }
 
     @Test
     public void testIsAppCertificateFormula_string_true() {
@@ -430,8 +490,8 @@
     private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
         return new AppInstallMetadata.Builder()
                 .setPackageName("abc")
-                .setAppCertificate("abc")
-                .setInstallerCertificate("abc")
+                .setAppCertificates(Collections.singletonList("abc"))
+                .setInstallerCertificates(Collections.singletonList("abc"))
                 .setInstallerName("abc")
                 .setVersionCode(-1)
                 .setIsPreInstalled(true);
diff --git a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
index f47dfdd..abc5fed 100644
--- a/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/CompoundFormulaTest.java
@@ -286,8 +286,8 @@
     private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
         return new AppInstallMetadata.Builder()
                 .setPackageName("abc")
-                .setAppCertificate("abc")
-                .setInstallerCertificate("abc")
+                .setAppCertificates(Collections.singletonList("abc"))
+                .setInstallerCertificates(Collections.singletonList("abc"))
                 .setInstallerName("abc")
                 .setVersionCode(-1)
                 .setIsPreInstalled(true);
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
index 2c956c9..669138c 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
@@ -148,6 +148,15 @@
         assertEquals(SMALLEST_SCREEN_WIDTH_DP_UNDEFINED, config.smallestScreenWidthDp);
     }
 
+    @Test
+    public void testNightModeHelper() {
+        Configuration config = new Configuration();
+        config.uiMode = Configuration.UI_MODE_NIGHT_YES;
+        assertTrue(config.isNightModeActive());
+        config.uiMode = Configuration.UI_MODE_NIGHT_NO;
+        assertFalse(config.isNightModeActive());
+    }
+
     private void dumpDebug(File f, Configuration config) throws Exception {
         final AtomicFile af = new AtomicFile(f);
         FileOutputStream fos = af.startWrite();
diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
index 49fb75b..b8dbfd3 100644
--- a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
@@ -466,179 +466,7 @@
             // expected
         }
     }
-    
-    @MediumTest
-    public void testTokenize() throws Exception {
-        Cursor c;
-        mDatabase.execSQL("CREATE TABLE tokens (" +
-                "token TEXT COLLATE unicode," +
-                "source INTEGER," +
-                "token_index INTEGER," +
-                "tag TEXT" +
-                ");");
-        mDatabase.execSQL("CREATE TABLE tokens_no_index (" +
-                "token TEXT COLLATE unicode," +
-                "source INTEGER" +
-                ");");
-        
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE(NULL, NULL, NULL, NULL)", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT _TOKENIZE('tokens', NULL, NULL, NULL)", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 10, NULL, NULL)", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 10, 'some string', NULL)", null));
-     
-        Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 11, 'some string ok', ' ', 1, 'foo')", null));
-        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT _TOKENIZE('tokens', 11, 'second field', ' ', 1, 'bar')", null));
 
-        Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT _TOKENIZE('tokens_no_index', 20, 'some string ok', ' ')", null));
-        Assert.assertEquals(3, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT _TOKENIZE('tokens_no_index', 21, 'foo bar baz', ' ', 0)", null));
-
-        // test Chinese
-        String chinese = new String("\u4eac\u4ec5 \u5c3d\u5f84\u60ca"); 
-        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 12,'" + chinese + "', ' ', 1)", null));
-        
-        String icustr = new String("Fr\u00e9d\u00e9ric Hj\u00f8nnev\u00e5g");
-        
-        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT _TOKENIZE('tokens', 13, '" + icustr + "', ' ', 1)", null));
-        
-        Assert.assertEquals(9, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens;", null));      
-
-        String key = DatabaseUtils.getHexCollationKey("Frederic Hjonneva");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));      
-        Assert.assertEquals(13, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        key = DatabaseUtils.getHexCollationKey("Hjonneva");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(13, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        
-        key = DatabaseUtils.getHexCollationKey("some string ok");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-        key = DatabaseUtils.getHexCollationKey("string");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-        key = DatabaseUtils.getHexCollationKey("ok");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(2, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("foo", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-
-        key = DatabaseUtils.getHexCollationKey("second field");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("bar", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-        key = DatabaseUtils.getHexCollationKey("field");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(11, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals("bar", DatabaseUtils.stringForQuery(mDatabase,
-                "SELECT tag from tokens where token GLOB '" + key + "*'", null));
-
-        key = DatabaseUtils.getHexCollationKey(chinese);
-        String[] a = new String[1];
-        a[0] = key;
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token= ?", a));
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token= ?", a));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token= ?", a));
-        a[0] += "*";
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-             "SELECT count(*) from tokens where token GLOB ?", a));        
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB ?", a));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB ?", a));
-
-       Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token= '" + key + "'", null));
-       Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-               "SELECT source from tokens where token= '" + key + "'", null));
-       Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-               "SELECT token_index from tokens where token= '" + key + "'", null));
-        
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));        
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        
-        key = DatabaseUtils.getHexCollationKey("\u4eac\u4ec5");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        
-        key = DatabaseUtils.getHexCollationKey("\u5c3d\u5f84\u60ca");
-        Log.d("DatabaseGeneralTest", "key = " + key);
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(12, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT token_index from tokens where token GLOB '" + key + "*'", null));
-        
-        Assert.assertEquals(0, DatabaseUtils.longForQuery(mDatabase, 
-                "SELECT count(*) from tokens where token GLOB 'ab*'", null));        
-
-        key = DatabaseUtils.getHexCollationKey("some string ok");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens_no_index where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(20, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens_no_index where token GLOB '" + key + "*'", null));
-
-        key = DatabaseUtils.getHexCollationKey("bar");
-        Assert.assertEquals(1, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT count(*) from tokens_no_index where token GLOB '" + key + "*'", null));
-        Assert.assertEquals(21, DatabaseUtils.longForQuery(mDatabase,
-                "SELECT source from tokens_no_index where token GLOB '" + key + "*'", null));
-    }
-    
     @MediumTest
     public void testTransactions() throws Exception {
         mDatabase.execSQL("CREATE TABLE test (num INTEGER);");
diff --git a/core/tests/coretests/src/android/view/CutoutSpecificationTest.java b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
new file mode 100644
index 0000000..1f831bb
--- /dev/null
+++ b/core/tests/coretests/src/android/view/CutoutSpecificationTest.java
@@ -0,0 +1,262 @@
+/*
+ * 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.view;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.graphics.Rect;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class CutoutSpecificationTest {
+    private static final String WITHOUT_BIND_CUTOUT_SPECIFICATION = "M 0,0\n"
+            + "h 48\n"
+            + "v 48\n"
+            + "h -48\n"
+            + "z\n"
+            + "@left\n"
+            + "@center_vertical\n"
+            + "M 0,0\n"
+            + "h 48\n"
+            + "v 48\n"
+            + "h -48\n"
+            + "z\n"
+            + "@left\n"
+            + "@center_vertical\n"
+            + "M 0,0\n"
+            + "h -48\n"
+            + "v 48\n"
+            + "h 48\n"
+            + "z\n"
+            + "@right\n"
+            + "@dp";
+    private static final String WITH_BIND_CUTOUT_SPECIFICATION = "M 0,0\n"
+            + "h 48\n"
+            + "v 48\n"
+            + "h -48\n"
+            + "z\n"
+            + "@left\n"
+            + "@center_vertical\n"
+            + "M 0,0\n"
+            + "h 48\n"
+            + "v 48\n"
+            + "h -48\n"
+            + "z\n"
+            + "@left\n"
+            + "@bind_left_cutout\n"
+            + "@center_vertical\n"
+            + "M 0,0\n"
+            + "h -48\n"
+            + "v 48\n"
+            + "h 48\n"
+            + "z\n"
+            + "@right\n"
+            + "@bind_right_cutout\n"
+            + "@dp";
+    private static final String CORNER_CUTOUT_SPECIFICATION = "M 0,0\n"
+            + "h 1\n"
+            + "v 1\n"
+            + "h -1\n"
+            + "z\n"
+            + "@left\n"
+            + "@cutout\n"
+            + "M 0, 0\n"
+            + "h -2\n"
+            + "v 2\n"
+            + "h 2\n"
+            + "z\n"
+            + "@right\n"
+            + "@bind_right_cutout\n"
+            + "@cutout\n"
+            + "M 0, 200\n"
+            + "h 3\n"
+            + "v -3\n"
+            + "h -3\n"
+            + "z\n"
+            + "@left\n"
+            + "@bind_left_cutout\n"
+            + "@bottom\n"
+            + "M 0, 0\n"
+            + "h -4\n"
+            + "v -4\n"
+            + "h 4\n"
+            + "z\n"
+            + "@right\n"
+            + "@dp";
+
+    private CutoutSpecification.Parser mParser;
+
+    /**
+     * Setup the necessary member field used by test methods.
+     */
+    @Before
+    public void setUp() {
+        mParser = new CutoutSpecification.Parser(3.5f, 1080, 1920);
+    }
+
+    @Test
+    public void parse_nullString_shouldTriggerException() {
+        assertThrows(NullPointerException.class, () -> mParser.parse(null));
+    }
+
+    @Test
+    public void parse_emptyString_pathShouldBeNull() {
+        CutoutSpecification cutoutSpecification = mParser.parse("");
+        assertThat(cutoutSpecification.getPath()).isNull();
+    }
+
+    @Test
+    public void parse_withoutBindMarker_shouldHaveNoLeftBound() {
+        CutoutSpecification cutoutSpecification = mParser.parse(WITHOUT_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getLeftBound()).isNull();
+    }
+
+    @Test
+    public void parse_withoutBindMarker_shouldHaveNoRightBound() {
+        CutoutSpecification cutoutSpecification = mParser.parse(WITHOUT_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getRightBound()).isNull();
+    }
+
+    @Test
+    public void parse_withBindMarker_shouldHaveLeftBound() {
+        CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getLeftBound()).isEqualTo(new Rect(0, 960, 168, 1128));
+    }
+
+    @Test
+    public void parse_withBindMarker_shouldHaveRightBound() {
+        CutoutSpecification cutoutSpecification = mParser.parse(WITH_BIND_CUTOUT_SPECIFICATION);
+        assertThat(cutoutSpecification.getRightBound()).isEqualTo(new Rect(912, 960, 1080, 1128));
+    }
+
+    @Test
+    public void parse_tallCutout_shouldBeDone() {
+        CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+                + "L -48, 0\n"
+                + "L -44.3940446283, 36.0595537175\n"
+                + "C -43.5582133885, 44.4178661152 -39.6, 48.0 -31.2, 48.0\n"
+                + "L 31.2, 48.0\n"
+                + "C 39.6, 48.0 43.5582133885, 44.4178661152 44.3940446283, 36.0595537175\n"
+                + "L 48, 0\n"
+                + "Z\n"
+                + "@dp");
+
+        assertThat(cutoutSpecification.getTopBound().height()).isEqualTo(168);
+    }
+
+    @Test
+    public void parse_wideCutout_shouldBeDone() {
+        CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+                + "L -72, 0\n"
+                + "L -69.9940446283, 20.0595537175\n"
+                + "C -69.1582133885, 28.4178661152 -65.2, 32.0 -56.8, 32.0\n"
+                + "L 56.8, 32.0\n"
+                + "C 65.2, 32.0 69.1582133885, 28.4178661152 69.9940446283, 20.0595537175\n"
+                + "L 72, 0\n"
+                + "Z\n"
+                + "@dp");
+
+        assertThat(cutoutSpecification.getTopBound().width()).isEqualTo(504);
+    }
+
+    @Test
+    public void parse_narrowCutout_shouldBeDone() {
+        CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+                + "L -24, 0\n"
+                + "L -21.9940446283, 20.0595537175\n"
+                + "C -21.1582133885, 28.4178661152 -17.2, 32.0 -8.8, 32.0\n"
+                + "L 8.8, 32.0\n"
+                + "C 17.2, 32.0 21.1582133885, 28.4178661152 21.9940446283, 20.0595537175\n"
+                + "L 24, 0\n"
+                + "Z\n"
+                + "@dp");
+
+        assertThat(cutoutSpecification.getTopBound().width()).isEqualTo(168);
+    }
+
+    @Test
+    public void parse_doubleCutout_shouldBeDone() {
+        CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+                + "L -72, 0\n"
+                + "L -69.9940446283, 20.0595537175\n"
+                + "C -69.1582133885, 28.4178661152 -65.2, 32.0 -56.8, 32.0\n"
+                + "L 56.8, 32.0\n"
+                + "C 65.2, 32.0 69.1582133885, 28.4178661152 69.9940446283, 20.0595537175\n"
+                + "L 72, 0\n"
+                + "Z\n"
+                + "@bottom\n"
+                + "M 0,0\n"
+                + "L -72, 0\n"
+                + "L -69.9940446283, -20.0595537175\n"
+                + "C -69.1582133885, -28.4178661152 -65.2, -32.0 -56.8, -32.0\n"
+                + "L 56.8, -32.0\n"
+                + "C 65.2, -32.0 69.1582133885, -28.4178661152 69.9940446283, -20"
+                + ".0595537175\n"
+                + "L 72, 0\n"
+                + "Z\n"
+                + "@dp");
+
+        assertThat(cutoutSpecification.getTopBound().height()).isEqualTo(112);
+    }
+
+    @Test
+    public void parse_cornerCutout_shouldBeDone() {
+        CutoutSpecification cutoutSpecification = mParser.parse("M 0,0\n"
+                + "L -48, 0\n"
+                + "C -48,48 -48,48 0,48\n"
+                + "Z\n"
+                + "@dp\n"
+                + "@right");
+
+        assertThat(cutoutSpecification.getTopBound().height()).isEqualTo(168);
+    }
+
+    @Test
+    public void parse_holeCutout_shouldBeDone() {
+        CutoutSpecification cutoutSpecification = mParser.parse("M 20.0,20.0\n"
+                + "h 136\n"
+                + "v 136\n"
+                + "h -136\n"
+                + "Z\n"
+                + "@left");
+
+        assertThat(cutoutSpecification.getSafeInset()).isEqualTo(new Rect(0, 156, 0, 0));
+    }
+
+    @Test
+    public void getSafeInset_shortEdgeIsTopBottom_shouldMatchExpectedInset() {
+        CutoutSpecification cutoutSpecification =
+                new CutoutSpecification.Parser(2f, 200, 400)
+                        .parse(CORNER_CUTOUT_SPECIFICATION);
+
+        assertThat(cutoutSpecification.getSafeInset())
+                .isEqualTo(new Rect(0, 4, 0, 8));
+    }
+
+    @Test
+    public void getSafeInset_shortEdgeIsLeftRight_shouldMatchExpectedInset() {
+        CutoutSpecification cutoutSpecification =
+                new CutoutSpecification.Parser(2f, 400, 200)
+                        .parse(CORNER_CUTOUT_SPECIFICATION);
+
+        assertThat(cutoutSpecification.getSafeInset())
+                .isEqualTo(new Rect(6, 0, 8, 0));
+    }
+}
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index 12c057f..02ffc00 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -20,11 +20,11 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.anyInt;
 
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.UserHandle;
-import android.text.Spannable;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
 
@@ -41,6 +41,7 @@
 @RunWith(AndroidJUnit4.class)
 public class EditorInfoTest {
     private static final int TEST_USER_ID = 42;
+    private static final int LONG_EXP_TEXT_LENGTH = EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH * 2;
 
     /**
      * Makes sure that {@code null} {@link EditorInfo#targetInputMethodUser} can be copied via
@@ -79,8 +80,8 @@
     }
 
     @Test
-    public void testNullTextInputComposeInitialSurroundingText() {
-        final Spannable testText = null;
+    public void setInitialText_nullInputText_throwsException() {
+        final CharSequence testText = null;
         final EditorInfo editorInfo = new EditorInfo();
 
         try {
@@ -92,56 +93,75 @@
     }
 
     @Test
-    public void testNonNullTextInputComposeInitialSurroundingText() {
-        final Spannable testText = createTestText(/* prependLength= */ 0,
-                EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH);
-        final EditorInfo editorInfo = new EditorInfo();
+    public void setInitialText_cursorAtHead_dividesByCursorPosition() {
+        final CharSequence testText = createTestText(EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH);
 
-        // Cursor at position 0.
-        int selectionLength = 0;
+        final EditorInfo editorInfo = new EditorInfo();
+        final int selectionLength = 0;
         editorInfo.initialSelStart = 0;
         editorInfo.initialSelEnd = editorInfo.initialSelStart + selectionLength;
-        int expectedTextBeforeCursorLength = 0;
-        int expectedTextAfterCursorLength = testText.length();
+        final int expectedTextBeforeCursorLength = 0;
+        final int expectedTextAfterCursorLength = testText.length();
 
         editorInfo.setInitialSurroundingText(testText);
 
         assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
                 expectedTextAfterCursorLength);
+    }
 
-        // Cursor at the end.
+    @Test
+    public void setInitialText_cursorAtTail_dividesByCursorPosition() {
+        final CharSequence testText = createTestText(EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH);
+        final EditorInfo editorInfo = new EditorInfo();
+        final int selectionLength = 0;
         editorInfo.initialSelStart = testText.length() - selectionLength;
         editorInfo.initialSelEnd = testText.length();
-        expectedTextBeforeCursorLength = testText.length();
-        expectedTextAfterCursorLength = 0;
+        final int expectedTextBeforeCursorLength = testText.length();
+        final int expectedTextAfterCursorLength = 0;
 
         editorInfo.setInitialSurroundingText(testText);
 
         assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
                 expectedTextAfterCursorLength);
+    }
 
-        // Cursor at the middle.
-        selectionLength = 2;
+    @Test
+    public void setInitialText_cursorAtMiddle_dividesByCursorPosition() {
+        final CharSequence testText = createTestText(EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH);
+        final EditorInfo editorInfo = new EditorInfo();
+        final int selectionLength = 2;
         editorInfo.initialSelStart = testText.length() / 2;
         editorInfo.initialSelEnd = editorInfo.initialSelStart + selectionLength;
-        expectedTextBeforeCursorLength = editorInfo.initialSelStart;
-        expectedTextAfterCursorLength = testText.length() - editorInfo.initialSelEnd;
+        final int expectedTextBeforeCursorLength = editorInfo.initialSelStart;
+        final int expectedTextAfterCursorLength = testText.length() - editorInfo.initialSelEnd;
 
         editorInfo.setInitialSurroundingText(testText);
 
         assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
                 expectedTextAfterCursorLength);
+    }
 
-        // Accidentally swap selection start and end.
+    @Test
+    public void setInitialText_incorrectCursorOrder_correctsThenDivide() {
+        final CharSequence testText = createTestText(EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH);
+        final EditorInfo editorInfo = new EditorInfo();
+        final int selectionLength = 2;
         editorInfo.initialSelEnd = testText.length() / 2;
         editorInfo.initialSelStart = editorInfo.initialSelEnd + selectionLength;
+        final int expectedTextBeforeCursorLength = testText.length() / 2;
+        final int expectedTextAfterCursorLength = testText.length() - testText.length() / 2
+                - selectionLength;
 
         editorInfo.setInitialSurroundingText(testText);
 
         assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
                 expectedTextAfterCursorLength);
+    }
 
-        // Invalid cursor position.
+    @Test
+    public void setInitialText_invalidCursorPosition_returnsNull() {
+        final CharSequence testText = createTestText(EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH);
+        final EditorInfo editorInfo = new EditorInfo();
         editorInfo.initialSelStart = -1;
 
         editorInfo.setInitialSurroundingText(testText);
@@ -153,64 +173,33 @@
     }
 
     @Test
-    public void testTooLongTextInputComposeInitialSurroundingText() {
-        final Spannable testText = createTestText(/* prependLength= */ 0,
-                EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH + 2);
+    public void setOverSizeInitialText_cursorAtMiddle_dividesProportionately() {
+        final CharSequence testText = createTestText(EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH + 2);
         final EditorInfo editorInfo = new EditorInfo();
-
-        // Cursor at position 0.
-        int selectionLength = 0;
-        editorInfo.initialSelStart = 0;
-        editorInfo.initialSelEnd = 0 + selectionLength;
-        int expectedTextBeforeCursorLength = 0;
-        int expectedTextAfterCursorLength = editorInfo.MEMORY_EFFICIENT_TEXT_LENGTH;
-
-        editorInfo.setInitialSurroundingText(testText);
-
-        assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
-                expectedTextAfterCursorLength);
-
-        // Cursor at the end.
-        editorInfo.initialSelStart = testText.length() - selectionLength;
-        editorInfo.initialSelEnd = testText.length();
-        expectedTextBeforeCursorLength = editorInfo.MEMORY_EFFICIENT_TEXT_LENGTH;
-        expectedTextAfterCursorLength = 0;
-
-        editorInfo.setInitialSurroundingText(testText);
-
-        assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
-                expectedTextAfterCursorLength);
-
-        // Cursor at the middle.
-        selectionLength = 2;
+        final int selectionLength = 2;
         editorInfo.initialSelStart = testText.length() / 2;
         editorInfo.initialSelEnd = editorInfo.initialSelStart + selectionLength;
-        expectedTextBeforeCursorLength = Math.min(editorInfo.initialSelStart,
+        final int expectedTextBeforeCursorLength = Math.min(editorInfo.initialSelStart,
                 (int) (0.8 * (EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH - selectionLength)));
-        expectedTextAfterCursorLength = EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH
+        final int expectedTextAfterCursorLength = EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH
                 - expectedTextBeforeCursorLength - selectionLength;
 
         editorInfo.setInitialSurroundingText(testText);
 
         assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
                 expectedTextAfterCursorLength);
+    }
 
-        // Accidentally swap selection start and end.
-        editorInfo.initialSelEnd = testText.length() / 2;
-        editorInfo.initialSelStart = editorInfo.initialSelEnd + selectionLength;
-
-        editorInfo.setInitialSurroundingText(testText);
-
-        assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
-                expectedTextAfterCursorLength);
-
-        // Selection too long, selected text should be dropped.
-        selectionLength = EditorInfo.MAX_INITIAL_SELECTION_LENGTH + 1;
+    @Test
+    public void setOverSizeInitialText_overSizeSelection_dropsSelection() {
+        final CharSequence testText = createTestText(EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH + 2);
+        final EditorInfo editorInfo = new EditorInfo();
+        final int selectionLength = EditorInfo.MAX_INITIAL_SELECTION_LENGTH + 1;
         editorInfo.initialSelStart = testText.length() / 2;
         editorInfo.initialSelEnd = editorInfo.initialSelStart + selectionLength;
-        expectedTextBeforeCursorLength = Math.min(editorInfo.initialSelStart,
+        final int expectedTextBeforeCursorLength = Math.min(editorInfo.initialSelStart,
                 (int) (0.8 * EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH));
-        expectedTextAfterCursorLength = testText.length() - editorInfo.initialSelEnd;
+        final int expectedTextAfterCursorLength = testText.length() - editorInfo.initialSelEnd;
 
         editorInfo.setInitialSurroundingText(testText);
 
@@ -219,34 +208,59 @@
     }
 
     @Test
-    public void testTooLongSubTextInputComposeInitialSurroundingText() {
-        final int prependLength = 5;
-        final int subTextLength = EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH;
-        final Spannable fullText = createTestText(prependLength, subTextLength);
+    public void setInitialSubText_trimmedSubText_dividesByOriginalCursorPosition() {
+        final String prefixString = "prefix";
+        final CharSequence subText = createTestText(EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH);
+        final CharSequence originalText = TextUtils.concat(prefixString, subText);
         final EditorInfo editorInfo = new EditorInfo();
-        // Cursor at the middle.
-        final int selectionLength = 2;
-        editorInfo.initialSelStart = fullText.length() / 2;
-        editorInfo.initialSelEnd = editorInfo.initialSelStart + selectionLength;
-        // #prependLength characters will be trimmed out.
-        final Spannable expectedTextBeforeCursor = createExpectedText(/* startNumber= */0,
-                editorInfo.initialSelStart - prependLength);
-        final Spannable expectedSelectedText = createExpectedText(
-                editorInfo.initialSelStart - prependLength, selectionLength);
-        final Spannable expectedTextAfterCursor = createExpectedText(
-                editorInfo.initialSelEnd - prependLength,
-                fullText.length() - editorInfo.initialSelEnd);
+        final int selLength = 2;
+        editorInfo.initialSelStart = originalText.length() / 2;
+        editorInfo.initialSelEnd = editorInfo.initialSelStart + selLength;
+        final CharSequence expectedTextBeforeCursor = createExpectedText(/* startNumber= */0,
+                editorInfo.initialSelStart - prefixString.length());
+        final CharSequence expectedSelectedText = createExpectedText(
+                editorInfo.initialSelStart - prefixString.length(), selLength);
+        final CharSequence expectedTextAfterCursor = createExpectedText(
+                editorInfo.initialSelEnd - prefixString.length(),
+                originalText.length() - editorInfo.initialSelEnd);
 
-        editorInfo.setInitialSurroundingSubText(fullText.subSequence(prependLength,
-                fullText.length()), prependLength);
+        editorInfo.setInitialSurroundingSubText(subText, prefixString.length());
 
         assertTrue(TextUtils.equals(expectedTextBeforeCursor,
-                editorInfo.getInitialTextBeforeCursor(editorInfo.MEMORY_EFFICIENT_TEXT_LENGTH,
-                        InputConnection.GET_TEXT_WITH_STYLES)));
+                editorInfo.getInitialTextBeforeCursor(LONG_EXP_TEXT_LENGTH, anyInt())));
         assertTrue(TextUtils.equals(expectedSelectedText,
-                editorInfo.getInitialSelectedText(InputConnection.GET_TEXT_WITH_STYLES)));
+                editorInfo.getInitialSelectedText(anyInt())));
         assertTrue(TextUtils.equals(expectedTextAfterCursor,
-                editorInfo.getInitialTextAfterCursor(editorInfo.MEMORY_EFFICIENT_TEXT_LENGTH,
+                editorInfo.getInitialTextAfterCursor(LONG_EXP_TEXT_LENGTH, anyInt())));
+    }
+
+    @Test
+    public void initialSurroundingText_wrapIntoParcel_staysIntact() {
+        // EditorInfo.InitialSurroundingText is not visible to test class. But all its key elements
+        // must stay intact for its getter methods to return correct value and it will be wrapped
+        // into its outer class for parcel transfer, therefore we can verify its parcel
+        // wrapping/unwrapping logic through its outer class.
+        final CharSequence testText = createTestText(EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH);
+        final EditorInfo sourceEditorInfo = new EditorInfo();
+        final int selectionLength = 2;
+        sourceEditorInfo.initialSelStart = testText.length() / 2;
+        sourceEditorInfo.initialSelEnd = sourceEditorInfo.initialSelStart + selectionLength;
+        sourceEditorInfo.setInitialSurroundingText(testText);
+
+        final EditorInfo targetEditorInfo = cloneViaParcel(sourceEditorInfo);
+
+        assertTrue(TextUtils.equals(
+                sourceEditorInfo.getInitialTextBeforeCursor(LONG_EXP_TEXT_LENGTH,
+                        InputConnection.GET_TEXT_WITH_STYLES),
+                targetEditorInfo.getInitialTextBeforeCursor(LONG_EXP_TEXT_LENGTH,
+                        InputConnection.GET_TEXT_WITH_STYLES)));
+        assertTrue(TextUtils.equals(
+                sourceEditorInfo.getInitialSelectedText(InputConnection.GET_TEXT_WITH_STYLES),
+                targetEditorInfo.getInitialSelectedText(InputConnection.GET_TEXT_WITH_STYLES)));
+        assertTrue(TextUtils.equals(
+                sourceEditorInfo.getInitialTextAfterCursor(LONG_EXP_TEXT_LENGTH,
+                        InputConnection.GET_TEXT_WITH_STYLES),
+                targetEditorInfo.getInitialTextAfterCursor(LONG_EXP_TEXT_LENGTH,
                         InputConnection.GET_TEXT_WITH_STYLES)));
     }
 
@@ -254,12 +268,12 @@
             @Nullable Integer expectBeforeCursorLength, @Nullable Integer expectSelectionLength,
             @Nullable Integer expectAfterCursorLength) {
         final CharSequence textBeforeCursor =
-                editorInfo.getInitialTextBeforeCursor(editorInfo.MEMORY_EFFICIENT_TEXT_LENGTH,
+                editorInfo.getInitialTextBeforeCursor(LONG_EXP_TEXT_LENGTH,
                         InputConnection.GET_TEXT_WITH_STYLES);
         final CharSequence selectedText =
                 editorInfo.getInitialSelectedText(InputConnection.GET_TEXT_WITH_STYLES);
         final CharSequence textAfterCursor =
-                editorInfo.getInitialTextAfterCursor(editorInfo.MEMORY_EFFICIENT_TEXT_LENGTH,
+                editorInfo.getInitialTextAfterCursor(LONG_EXP_TEXT_LENGTH,
                         InputConnection.GET_TEXT_WITH_STYLES);
 
         if (expectBeforeCursorLength == null) {
@@ -281,19 +295,15 @@
         }
     }
 
-    private static Spannable createTestText(int prependLength, int surroundingLength) {
+    private static CharSequence createTestText(int surroundingLength) {
         final SpannableStringBuilder builder = new SpannableStringBuilder();
-        for (int i = 0; i < prependLength; i++) {
-            builder.append("a");
-        }
-
         for (int i = 0; i < surroundingLength; i++) {
             builder.append(Integer.toString(i % 10));
         }
         return builder;
     }
 
-    private static Spannable createExpectedText(int startNumber, int length) {
+    private static CharSequence createExpectedText(int startNumber, int length) {
         final SpannableStringBuilder builder = new SpannableStringBuilder();
         for (int i = startNumber; i < startNumber + length; i++) {
             builder.append(Integer.toString(i % 10));
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index a2d2355..8dbb5f5 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -206,6 +206,7 @@
                 blockModes,
                 userAuthenticationRequired,
                 (int) userAuthenticationValidityDurationSeconds,
+                keymasterHwEnforcedUserAuthenticators,
                 userAuthenticationRequirementEnforcedBySecureHardware,
                 userAuthenticationValidWhileOnBody,
                 trustedUserPresenceRequred,
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 450dd33..d683041 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -263,6 +263,7 @@
     private final boolean mRandomizedEncryptionRequired;
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
+    private final @KeyProperties.AuthEnum int mUserAuthenticationType;
     private final boolean mUserPresenceRequired;
     private final byte[] mAttestationChallenge;
     private final boolean mUniqueIdIncluded;
@@ -301,6 +302,7 @@
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
+            @KeyProperties.AuthEnum int userAuthenticationType,
             boolean userPresenceRequired,
             byte[] attestationChallenge,
             boolean uniqueIdIncluded,
@@ -352,6 +354,7 @@
         mUserAuthenticationRequired = userAuthenticationRequired;
         mUserPresenceRequired = userPresenceRequired;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+        mUserAuthenticationType = userAuthenticationType;
         mAttestationChallenge = Utils.cloneIfNotNull(attestationChallenge);
         mUniqueIdIncluded = uniqueIdIncluded;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
@@ -605,6 +608,22 @@
     }
 
     /**
+     * Gets the modes of authentication that can authorize use of this key. This has effect only if
+     * user authentication is required (see {@link #isUserAuthenticationRequired()}).
+     *
+     * <p>This authorization applies only to secret key and private key operations. Public key
+     * operations are not restricted.
+     *
+     * @return integer representing the bitwse OR of all acceptable authentication types for the
+     *         key.
+     *
+     * @see #isUserAuthenticationRequired()
+     * @see Builder#setUserAuthenticationParameters(int, int)
+     */
+    public @KeyProperties.AuthEnum int getUserAuthenticationType() {
+        return mUserAuthenticationType;
+    }
+    /**
      * Returns {@code true} if the key is authorized to be used only if a test of user presence has
      * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
      * It requires that the KeyStore implementation have a direct way to validate the user presence
@@ -746,6 +765,7 @@
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
         private int mUserAuthenticationValidityDurationSeconds = -1;
+        private @KeyProperties.AuthEnum int mUserAuthenticationType;
         private boolean mUserPresenceRequired = false;
         private byte[] mAttestationChallenge = null;
         private boolean mUniqueIdIncluded = false;
@@ -810,6 +830,7 @@
             mUserAuthenticationRequired = sourceSpec.isUserAuthenticationRequired();
             mUserAuthenticationValidityDurationSeconds =
                 sourceSpec.getUserAuthenticationValidityDurationSeconds();
+            mUserAuthenticationType = sourceSpec.getUserAuthenticationType();
             mUserPresenceRequired = sourceSpec.isUserPresenceRequired();
             mAttestationChallenge = sourceSpec.getAttestationChallenge();
             mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded();
@@ -1207,14 +1228,62 @@
          * @see BiometricPrompt
          * @see BiometricPrompt.CryptoObject
          * @see KeyguardManager
+         * @deprecated See {@link #setUserAuthenticationParameters(int, int)}
          */
+        @Deprecated
         @NonNull
         public Builder setUserAuthenticationValidityDurationSeconds(
                 @IntRange(from = -1) int seconds) {
             if (seconds < -1) {
                 throw new IllegalArgumentException("seconds must be -1 or larger");
             }
-            mUserAuthenticationValidityDurationSeconds = seconds;
+            if (seconds == -1) {
+                return setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG);
+            }
+            return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_BIOMETRIC_STRONG);
+        }
+
+        /**
+         * Sets the duration of time (seconds) and authorization type for which this key is
+         * authorized to be used after the user is successfully authenticated. This has effect if
+         * the key requires user authentication for its use (see
+         * {@link #setUserAuthenticationRequired(boolean)}).
+         *
+         * <p>By default, if user authentication is required, it must take place for every use of
+         * the key.
+         *
+         * <p>These cryptographic operations will throw {@link UserNotAuthenticatedException} during
+         * initialization if the user needs to be authenticated to proceed. This situation can be
+         * resolved by the user authenticating with the appropriate biometric or credential as
+         * required by the key. See {@link BiometricPrompt.Builder#setAllowedAuthenticators(int)}
+         * and {@link BiometricManager.Authenticators}.
+         *
+         * <p>Once resolved, initializing a new cryptographic operation using this key (or any other
+         * key which is authorized to be used for a fixed duration of time after user
+         * authentication) should succeed provided the user authentication flow completed
+         * successfully.
+         *
+         * @param timeout duration in seconds or {@code 0} if user authentication must take place
+         *        for every use of the key. {@code -1} is also accepted for legacy purposes. It is
+         *        functionally the same as {@code 0}.
+         * @param type set of authentication types which can authorize use of the key. See
+         *        {@link KeyProperties}.{@code AUTH} flags.
+         *
+         * @see #setUserAuthenticationRequired(boolean)
+         * @see BiometricPrompt
+         * @see BiometricPrompt.CryptoObject
+         * @see KeyguardManager
+         */
+        @NonNull
+        public Builder setUserAuthenticationParameters(@IntRange(from = -1) int timeout,
+                                                       @KeyProperties.AuthEnum int type) {
+            if (timeout < -1) {
+                throw new IllegalArgumentException("timeout must be -1 or larger");
+            } else if (timeout == -1) {
+                timeout = 0;
+            }
+            mUserAuthenticationValidityDurationSeconds = timeout;
+            mUserAuthenticationType = type;
             return this;
         }
 
@@ -1392,6 +1461,7 @@
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
                     mUserAuthenticationValidityDurationSeconds,
+                    mUserAuthenticationType,
                     mUserPresenceRequired,
                     mAttestationChallenge,
                     mUniqueIdIncluded,
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index 0a75cd5..d891a25 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -78,6 +78,7 @@
     private final @KeyProperties.BlockModeEnum String[] mBlockModes;
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
+    private final @KeyProperties.AuthEnum int mUserAuthenticationType;
     private final boolean mUserAuthenticationRequirementEnforcedBySecureHardware;
     private final boolean mUserAuthenticationValidWhileOnBody;
     private final boolean mTrustedUserPresenceRequired;
@@ -101,6 +102,7 @@
             @KeyProperties.BlockModeEnum String[] blockModes,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
+            @KeyProperties.AuthEnum int userAuthenticationType,
             boolean userAuthenticationRequirementEnforcedBySecureHardware,
             boolean userAuthenticationValidWhileOnBody,
             boolean trustedUserPresenceRequired,
@@ -122,6 +124,7 @@
         mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
         mUserAuthenticationRequired = userAuthenticationRequired;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+        mUserAuthenticationType = userAuthenticationType;
         mUserAuthenticationRequirementEnforcedBySecureHardware =
                 userAuthenticationRequirementEnforcedBySecureHardware;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
@@ -301,6 +304,22 @@
     }
 
     /**
+     * Gets the acceptable user authentication types for which this key can be authorized to be
+     * used. This has effect only if user authentication is required (see
+     * {@link #isUserAuthenticationRequired()}).
+     *
+     * <p>This authorization applies only to secret key and private key operations. Public key
+     * operations are not restricted.
+     *
+     * @return integer representing the accepted forms of user authentication for this key
+     *
+     * @see #isUserAuthenticationRequired()
+     */
+    public @KeyProperties.AuthEnum int getUserAuthenticationType() {
+        return mUserAuthenticationType;
+    }
+
+    /**
      * Returns {@code true} if the requirement that this key can only be used if the user has been
      * authenticated is enforced by secure hardware (e.g., Trusted Execution Environment (TEE) or
      * Secure Element (SE)).
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index f12a659..c58a123 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -39,6 +39,27 @@
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "AUTH_" }, value = {
+            AUTH_BIOMETRIC_STRONG,
+            AUTH_DEVICE_CREDENTIAL,
+    })
+    public @interface AuthEnum {}
+
+    /**
+     * The non-biometric credential used to secure the device (i.e., PIN, pattern, or password)
+     */
+    public static final int AUTH_DEVICE_CREDENTIAL = 1 << 0;
+
+    /**
+     * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
+     * requirements for <strong>Strong</strong>, as defined by the Android CDD.
+     */
+    public static final int AUTH_BIOMETRIC_STRONG = 1 << 1;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, prefix = { "PURPOSE_" }, value = {
             PURPOSE_ENCRYPT,
             PURPOSE_DECRYPT,
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 26181a6..e230b7c 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -225,6 +225,7 @@
     private final @KeyProperties.BlockModeEnum String[] mBlockModes;
     private final boolean mRandomizedEncryptionRequired;
     private final boolean mUserAuthenticationRequired;
+    private final @KeyProperties.AuthEnum int mUserAuthenticationType;
     private final int mUserAuthenticationValidityDurationSeconds;
     private final boolean mUserPresenceRequred;
     private final boolean mUserAuthenticationValidWhileOnBody;
@@ -246,6 +247,7 @@
             @KeyProperties.BlockModeEnum String[] blockModes,
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
+            @KeyProperties.AuthEnum int userAuthenticationType,
             int userAuthenticationValidityDurationSeconds,
             boolean userPresenceRequred,
             boolean userAuthenticationValidWhileOnBody,
@@ -267,6 +269,7 @@
         mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
         mRandomizedEncryptionRequired = randomizedEncryptionRequired;
         mUserAuthenticationRequired = userAuthenticationRequired;
+        mUserAuthenticationType = userAuthenticationType;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
         mUserPresenceRequred = userPresenceRequred;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
@@ -429,6 +432,10 @@
         return mUserConfirmationRequired;
     }
 
+    public @KeyProperties.AuthEnum int getUserAuthenticationType() {
+        return mUserAuthenticationType;
+    }
+
     /**
      * Gets the duration of time (seconds) for which this key is authorized to be used after the
      * user is successfully authenticated. This has effect only if user authentication is required
@@ -555,6 +562,7 @@
         private @KeyProperties.BlockModeEnum String[] mBlockModes;
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
+        private @KeyProperties.AuthEnum int mUserAuthenticationType;
         private int mUserAuthenticationValidityDurationSeconds = -1;
         private boolean mUserPresenceRequired = false;
         private boolean mUserAuthenticationValidWhileOnBody;
@@ -850,14 +858,62 @@
          * @see BiometricPrompt
          * @see BiometricPrompt.CryptoObject
          * @see KeyguardManager
+         * @deprecated See {@link #setUserAuthenticationParameters(int, int)}
          */
+        @Deprecated
         @NonNull
         public Builder setUserAuthenticationValidityDurationSeconds(
                 @IntRange(from = -1) int seconds) {
             if (seconds < -1) {
                 throw new IllegalArgumentException("seconds must be -1 or larger");
             }
-            mUserAuthenticationValidityDurationSeconds = seconds;
+            if (seconds == -1) {
+                return setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG);
+            }
+            return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_BIOMETRIC_STRONG);
+        }
+
+        /**
+         * Sets the duration of time (seconds) and authorization type for which this key is
+         * authorized to be used after the user is successfully authenticated. This has effect if
+         * the key requires user authentication for its use (see
+         * {@link #setUserAuthenticationRequired(boolean)}).
+         *
+         * <p>By default, if user authentication is required, it must take place for every use of
+         * the key.
+         *
+         * <p>These cryptographic operations will throw {@link UserNotAuthenticatedException} during
+         * initialization if the user needs to be authenticated to proceed. This situation can be
+         * resolved by the user authenticating with the appropriate biometric or credential as
+         * required by the key. See {@link BiometricPrompt.Builder#setAllowedAuthenticators(int)}
+         * and {@link BiometricManager.Authenticators}.
+         *
+         * <p>Once resolved, initializing a new cryptographic operation using this key (or any other
+         * key which is authorized to be used for a fixed duration of time after user
+         * authentication) should succeed provided the user authentication flow completed
+         * successfully.
+         *
+         * @param timeout duration in seconds or {@code 0} if user authentication must take place
+         *        for every use of the key. {@code -1} is also accepted for legacy purposes. It is
+         *        functionally the same as {@code 0}.
+         * @param type set of authentication types which can authorize use of the key. See
+         *        {@link KeyProperties}.{@code AUTH} flags.
+         *
+         * @see #setUserAuthenticationRequired(boolean)
+         * @see BiometricPrompt
+         * @see BiometricPrompt.CryptoObject
+         * @see KeyguardManager
+         */
+        @NonNull
+        public Builder setUserAuthenticationParameters(@IntRange(from = -1) int timeout,
+                                                       @KeyProperties.AuthEnum int type) {
+            if (timeout < -1) {
+                throw new IllegalArgumentException("timeout must be -1 or larger");
+            } else if (timeout == -1) {
+                timeout = 0;
+            }
+            mUserAuthenticationValidityDurationSeconds = timeout;
+            mUserAuthenticationType = type;
             return this;
         }
 
@@ -1002,6 +1058,7 @@
                     mBlockModes,
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
+                    mUserAuthenticationType,
                     mUserAuthenticationValidityDurationSeconds,
                     mUserPresenceRequired,
                     mUserAuthenticationValidWhileOnBody,
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index 79e48cd..37b1f23 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -88,17 +88,9 @@
      * Adds keymaster arguments to express the key's authorization policy supported by user
      * authentication.
      *
-     * @param userAuthenticationRequired whether user authentication is required to authorize the
-     *        use of the key.
-     * @param userAuthenticationValidityDurationSeconds duration of time (seconds) for which user
-     *        authentication is valid as authorization for using the key or {@code -1} if every
-     *        use of the key needs authorization.
-     * @param boundToSpecificSecureUserId if non-zero, specify which SID the key will be bound to,
-     *        overriding the default logic in this method where the key is bound to either the root
-     *        SID of the current user, or the fingerprint SID if explicit fingerprint authorization
-     *        is requested.
-     * @param userConfirmationRequired whether user confirmation is required to authorize the use
-     *        of the key.
+     * @param args The arguments sent to keymaster that need to be populated from the spec
+     * @param spec The user authentication relevant portions of the spec passed in from the caller.
+     *        This spec will be translated into the relevant keymaster tags to be loaded into args.
      * @throws IllegalStateException if user authentication is required but the system is in a wrong
      *         state (e.g., secure lock screen not set up) for generating or importing keys that
      *         require user authentication.
@@ -122,7 +114,7 @@
             return;
         }
 
-        if (spec.getUserAuthenticationValidityDurationSeconds() == -1) {
+        if (spec.getUserAuthenticationValidityDurationSeconds() == 0) {
             PackageManager pm = KeyStore.getApplicationContext().getPackageManager();
             // Every use of this key needs to be authorized by the user. This currently means
             // fingerprint or face auth.
@@ -168,7 +160,8 @@
                 args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
                         KeymasterArguments.toUint64(sids.get(i)));
             }
-            args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_BIOMETRIC);
+
+            args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType());
 
             if (spec.isUserAuthenticationValidWhileOnBody()) {
                 throw new ProviderException("Key validity extension while device is on-body is not "
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 98e4589..9c9773e 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -97,6 +97,7 @@
         out.writeBoolean(mSpec.isRandomizedEncryptionRequired());
         out.writeBoolean(mSpec.isUserAuthenticationRequired());
         out.writeInt(mSpec.getUserAuthenticationValidityDurationSeconds());
+        out.writeInt(mSpec.getUserAuthenticationType());
         out.writeBoolean(mSpec.isUserPresenceRequired());
         out.writeByteArray(mSpec.getAttestationChallenge());
         out.writeBoolean(mSpec.isUniqueIdIncluded());
@@ -153,6 +154,7 @@
         final boolean randomizedEncryptionRequired = in.readBoolean();
         final boolean userAuthenticationRequired = in.readBoolean();
         final int userAuthenticationValidityDurationSeconds = in.readInt();
+        final int userAuthenticationTypes = in.readInt();
         final boolean userPresenceRequired = in.readBoolean();
         final byte[] attestationChallenge = in.createByteArray();
         final boolean uniqueIdIncluded = in.readBoolean();
@@ -185,6 +187,7 @@
                 randomizedEncryptionRequired,
                 userAuthenticationRequired,
                 userAuthenticationValidityDurationSeconds,
+                userAuthenticationTypes,
                 userPresenceRequired,
                 attestationChallenge,
                 uniqueIdIncluded,
diff --git a/keystore/java/android/security/keystore/UserAuthArgs.java b/keystore/java/android/security/keystore/UserAuthArgs.java
index 6952060..c9e9bf0 100644
--- a/keystore/java/android/security/keystore/UserAuthArgs.java
+++ b/keystore/java/android/security/keystore/UserAuthArgs.java
@@ -28,6 +28,7 @@
 
     boolean isUserAuthenticationRequired();
     int getUserAuthenticationValidityDurationSeconds();
+    @KeyProperties.AuthEnum int getUserAuthenticationType();
     boolean isUserAuthenticationValidWhileOnBody();
     boolean isInvalidatedByBiometricEnrollment();
     boolean isUserConfirmationRequired();
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 8cfd2d8..3208662 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -992,6 +992,11 @@
   return bag;
 }
 
+static bool compare_bag_entries(const ResolvedBag::Entry& entry1,
+    const ResolvedBag::Entry& entry2) {
+  return entry1.key < entry2.key;
+}
+
 const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& child_resids) {
   auto cached_iter = cached_bags_.find(resid);
   if (cached_iter != cached_bags_.end()) {
@@ -1027,13 +1032,15 @@
   child_resids.push_back(resid);
 
   uint32_t parent_resid = dtohl(map->parent.ident);
-  if (parent_resid == 0 || std::find(child_resids.begin(), child_resids.end(), parent_resid)
+  if (parent_resid == 0U || std::find(child_resids.begin(), child_resids.end(), parent_resid)
       != child_resids.end()) {
-    // There is no parent or that a circular dependency exist, meaning there is nothing to
-    // inherit and we can do a simple copy of the entries in the map.
+    // There is no parent or a circular dependency exist, meaning there is nothing to inherit and
+    // we can do a simple copy of the entries in the map.
     const size_t entry_count = map_entry_end - map_entry;
     util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>(
         malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))};
+
+    bool sort_entries = false;
     ResolvedBag::Entry* new_entry = new_bag->entries;
     for (; map_entry != map_entry_end; ++map_entry) {
       uint32_t new_key = dtohl(map_entry->name.ident);
@@ -1059,8 +1066,15 @@
             new_entry->value.data, new_key);
         return nullptr;
       }
+      sort_entries = sort_entries ||
+          (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
       ++new_entry;
     }
+
+    if (sort_entries) {
+      std::sort(new_bag->entries, new_bag->entries + entry_count, compare_bag_entries);
+    }
+
     new_bag->type_spec_flags = entry.type_flags;
     new_bag->entry_count = static_cast<uint32_t>(entry_count);
     ResolvedBag* result = new_bag.get();
@@ -1091,6 +1105,7 @@
   const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count;
 
   // The keys are expected to be in sorted order. Merge the two bags.
+  bool sort_entries = false;
   while (map_entry != map_entry_end && parent_entry != parent_entry_end) {
     uint32_t child_key = dtohl(map_entry->name.ident);
     if (!is_internal_resid(child_key)) {
@@ -1123,6 +1138,8 @@
       memcpy(new_entry, parent_entry, sizeof(*new_entry));
     }
 
+    sort_entries = sort_entries ||
+        (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
     if (child_key >= parent_entry->key) {
       // Move to the next parent entry if we used it or it was overridden.
       ++parent_entry;
@@ -1153,6 +1170,8 @@
                                        new_entry->value.dataType, new_entry->value.data, new_key);
       return nullptr;
     }
+    sort_entries = sort_entries ||
+        (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key));
     ++map_entry;
     ++new_entry;
   }
@@ -1172,6 +1191,10 @@
         new_bag.release(), sizeof(ResolvedBag) + (actual_count * sizeof(ResolvedBag::Entry)))));
   }
 
+  if (sort_entries) {
+    std::sort(new_bag->entries, new_bag->entries + actual_count, compare_bag_entries);
+  }
+
   // Combine flags from the parent and our own bag.
   new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags;
   new_bag->entry_count = static_cast<uint32_t>(actual_count);
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 2f6f3df..35fea7a 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -285,6 +285,27 @@
   EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
 }
 
+TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) {
+  AssetManager2 assetmanager;
+
+  // libclient is built with lib_one and then lib_two in order.
+  // Reverse the order to test that proper package ID re-assignment is happening.
+  assetmanager.SetApkAssets(
+      {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()});
+
+  const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib);
+  ASSERT_NE(nullptr, bag);
+  ASSERT_EQ(bag->entry_count, 2u);
+
+  // First attribute comes from lib_two.
+  EXPECT_EQ(2, bag->entries[0].cookie);
+  EXPECT_EQ(0x02, get_package_id(bag->entries[0].key));
+
+  // The next two attributes come from lib_one.
+  EXPECT_EQ(2, bag->entries[1].cookie);
+  EXPECT_EQ(0x03, get_package_id(bag->entries[1].key));
+}
+
 TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) {
   AssetManager2 assetmanager;
 
diff --git a/libs/androidfw/tests/data/lib_two/R.h b/libs/androidfw/tests/data/lib_two/R.h
index 92b9cc1..fd5a910 100644
--- a/libs/androidfw/tests/data/lib_two/R.h
+++ b/libs/androidfw/tests/data/lib_two/R.h
@@ -30,16 +30,22 @@
     };
   };
 
+  struct integer {
+    enum : uint32_t {
+      bar = 0x02020000, // default
+    };
+  };
+
   struct string {
     enum : uint32_t {
-      LibraryString = 0x02020000,  // default
-      foo = 0x02020001, // default
+      LibraryString = 0x02030000,  // default
+      foo = 0x02030001, // default
     };
   };
 
   struct style {
     enum : uint32_t {
-      Theme = 0x02030000, // default
+      Theme = 0x02040000, // default
     };
   };
 };
diff --git a/libs/androidfw/tests/data/lib_two/lib_two.apk b/libs/androidfw/tests/data/lib_two/lib_two.apk
index 486c230..8193db6 100644
--- a/libs/androidfw/tests/data/lib_two/lib_two.apk
+++ b/libs/androidfw/tests/data/lib_two/lib_two.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/lib_two/res/values/values.xml b/libs/androidfw/tests/data/lib_two/res/values/values.xml
index 340d14c..4e1d69a 100644
--- a/libs/androidfw/tests/data/lib_two/res/values/values.xml
+++ b/libs/androidfw/tests/data/lib_two/res/values/values.xml
@@ -18,14 +18,17 @@
     <public type="attr" name="attr3" id="0x00010000" />
     <attr name="attr3" format="integer" />
 
-    <public type="string" name="LibraryString" id="0x00020000" />
+    <public type="integer" name="bar" id="0x00020000" />
+    <integer name="bar">1337</integer>
+
+    <public type="string" name="LibraryString" id="0x00030000" />
     <string name="LibraryString">Hi from library two</string>
 
-    <public type="string" name="foo" id="0x00020001" />
+    <public type="string" name="foo" id="0x00030001" />
     <string name="foo">Foo from lib_two</string>
 
-    <public type="style" name="Theme" id="0x02030000" />
+    <public type="style" name="Theme" id="0x00040000" />
     <style name="Theme">
-        <item name="com.android.lib_two:attr3">800</item>
+        <item name="com.android.lib_two:attr3">@integer/bar</item>
     </style>
 </resources>
diff --git a/libs/androidfw/tests/data/libclient/R.h b/libs/androidfw/tests/data/libclient/R.h
index 43d1f9b..e21b3eb 100644
--- a/libs/androidfw/tests/data/libclient/R.h
+++ b/libs/androidfw/tests/data/libclient/R.h
@@ -34,6 +34,7 @@
   struct style {
     enum : uint32_t {
       Theme = 0x7f020000,  // default
+      ThemeMultiLib = 0x7f020001,  // default
     };
   };
 
diff --git a/libs/androidfw/tests/data/libclient/libclient.apk b/libs/androidfw/tests/data/libclient/libclient.apk
index 1799024..4b9a8833 100644
--- a/libs/androidfw/tests/data/libclient/libclient.apk
+++ b/libs/androidfw/tests/data/libclient/libclient.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/libclient/res/values/values.xml b/libs/androidfw/tests/data/libclient/res/values/values.xml
index fead7c3..a29f473 100644
--- a/libs/androidfw/tests/data/libclient/res/values/values.xml
+++ b/libs/androidfw/tests/data/libclient/res/values/values.xml
@@ -27,6 +27,12 @@
       <item name="bar">@com.android.lib_one:string/foo</item>
     </style>
 
+    <public type="style" name="ThemeMultiLib" id="0x7f020001" />
+    <style name="ThemeMultiLib" >
+      <item name="com.android.lib_one:attr1">@com.android.lib_one:string/foo</item>
+      <item name="com.android.lib_two:attr3">@com.android.lib_two:integer/bar</item>
+    </style>
+
     <public type="string" name="foo_one" id="0x7f030000" />
     <string name="foo_one">@com.android.lib_one:string/foo</string>
 
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 301d1af..debb38b2 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -171,7 +171,6 @@
         "renderthread/RenderTask.cpp",
         "renderthread/TimeLord.cpp",
         "hwui/AnimatedImageDrawable.cpp",
-        "hwui/AnimatedImageThread.cpp",
         "hwui/Bitmap.cpp",
         "hwui/Canvas.cpp",
         "hwui/ImageDecoder.cpp",
@@ -213,6 +212,7 @@
         android: {
 
             srcs: [
+                "hwui/AnimatedImageThread.cpp",
                 "pipeline/skia/ATraceMemoryDump.cpp",
                 "pipeline/skia/GLFunctorDrawable.cpp",
                 "pipeline/skia/LayerDrawable.cpp",
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 4544bea..638de85 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -15,7 +15,9 @@
  */
 
 #include "AnimatedImageDrawable.h"
+#ifdef __ANDROID__ // Layoutlib does not support AnimatedImageThread
 #include "AnimatedImageThread.h"
+#endif
 
 #include "utils/TraceUtils.h"
 
@@ -160,8 +162,10 @@
     } else if (starting) {
         // The image has animated, and now is being reset. Queue up the first
         // frame, but keep showing the current frame until the first is ready.
+#ifdef __ANDROID__ // Layoutlib does not support AnimatedImageThread
         auto& thread = uirenderer::AnimatedImageThread::getInstance();
         mNextSnapshot = thread.reset(sk_ref_sp(this));
+#endif
     }
 
     bool finalFrame = false;
@@ -187,8 +191,10 @@
     }
 
     if (mRunning && !mNextSnapshot.valid()) {
+#ifdef __ANDROID__ // Layoutlib does not support AnimatedImageThread
         auto& thread = uirenderer::AnimatedImageThread::getInstance();
         mNextSnapshot = thread.decodeNextFrame(sk_ref_sp(this));
+#endif
     }
 
     if (!drawDirectly) {
diff --git a/location/java/android/location/AbstractListenerManager.java b/location/java/android/location/AbstractListenerManager.java
index 944ebf9..f075a53 100644
--- a/location/java/android/location/AbstractListenerManager.java
+++ b/location/java/android/location/AbstractListenerManager.java
@@ -27,6 +27,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
 
+import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
@@ -35,26 +36,34 @@
  *
  * @hide
  */
-abstract class AbstractListenerManager<T> {
+abstract class AbstractListenerManager<TRequest, TListener> {
 
-    private static class Registration<T> {
+    private static class Registration<TRequest, TListener> {
         private final Executor mExecutor;
-        @Nullable private volatile T mListener;
+        @Nullable private TRequest mRequest;
+        @Nullable private volatile TListener mListener;
 
-        private Registration(Executor executor, T listener) {
+        private Registration(@Nullable TRequest request, Executor executor, TListener listener) {
             Preconditions.checkArgument(listener != null, "invalid null listener/callback");
             Preconditions.checkArgument(executor != null, "invalid null executor");
             mExecutor = executor;
             mListener = listener;
+            mRequest = request;
+        }
+
+        @Nullable
+        public TRequest getRequest() {
+            return mRequest;
         }
 
         private void unregister() {
+            mRequest = null;
             mListener = null;
         }
 
-        private void execute(Consumer<T> operation) {
+        private void execute(Consumer<TListener> operation) {
             mExecutor.execute(() -> {
-                T listener = mListener;
+                TListener listener = mListener;
                 if (listener == null) {
                     return;
                 }
@@ -71,71 +80,135 @@
     }
 
     @GuardedBy("mListeners")
-    private final ArrayMap<Object, Registration<T>> mListeners = new ArrayMap<>();
+    private final ArrayMap<Object, Registration<TRequest, TListener>> mListeners =
+            new ArrayMap<>();
 
-    public boolean addListener(@NonNull T listener, @NonNull Handler handler)
+    @GuardedBy("mListeners")
+    @Nullable
+    private TRequest mMergedRequest;
+
+    public boolean addListener(@NonNull TListener listener, @NonNull Handler handler)
             throws RemoteException {
-        return addInternal(listener, handler);
+        return addInternal(/* request= */ null, listener, handler);
     }
 
-    public boolean addListener(@NonNull T listener, @NonNull Executor executor)
+    public boolean addListener(@NonNull TListener listener, @NonNull Executor executor)
             throws RemoteException {
-        return addInternal(listener, executor);
+        return addInternal(/* request= */ null, listener, executor);
     }
 
-    protected final boolean addInternal(@NonNull Object listener, @NonNull Handler handler)
-            throws RemoteException {
-        return addInternal(listener, new HandlerExecutor(handler));
+    public boolean addListener(@Nullable TRequest request, @NonNull TListener listener,
+            @NonNull Handler handler) throws RemoteException {
+        return addInternal(request, listener, handler);
     }
 
-    protected final boolean addInternal(@NonNull Object listener, @NonNull Executor executor)
+    public boolean addListener(@Nullable TRequest request, @NonNull TListener listener,
+            @NonNull Executor executor) throws RemoteException {
+        return addInternal(request, listener, executor);
+    }
+
+    protected final boolean addInternal(@Nullable TRequest request, @NonNull Object listener,
+            @NonNull Handler handler) throws RemoteException {
+        return addInternal(request, listener, new HandlerExecutor(handler));
+    }
+
+    protected final boolean addInternal(@Nullable TRequest request, @NonNull Object listener,
+            @NonNull Executor executor)
             throws RemoteException {
         Preconditions.checkArgument(listener != null, "invalid null listener/callback");
-        return addInternal(listener, new Registration<>(executor, convertKey(listener)));
+        return addInternal(listener, new Registration<>(request, executor, convertKey(listener)));
     }
 
-    private boolean addInternal(Object key, Registration<T> registration) throws RemoteException {
+    private boolean addInternal(Object key, Registration<TRequest, TListener> registration)
+            throws RemoteException {
         Preconditions.checkNotNull(registration);
 
         synchronized (mListeners) {
-            if (mListeners.isEmpty() && !registerService()) {
-                return false;
-            }
-            Registration<T> oldRegistration = mListeners.put(key, registration);
+            boolean initialRequest = mListeners.isEmpty();
+
+            Registration<TRequest, TListener> oldRegistration = mListeners.put(key, registration);
             if (oldRegistration != null) {
                 oldRegistration.unregister();
             }
+            TRequest merged = mergeRequests();
+
+            if (initialRequest || !Objects.equals(merged, mMergedRequest)) {
+                mMergedRequest = merged;
+                if (!initialRequest) {
+                    unregisterService();
+                }
+                registerService(mMergedRequest);
+            }
+
             return true;
         }
     }
 
     public void removeListener(Object listener) throws RemoteException {
         synchronized (mListeners) {
-            Registration<T> oldRegistration = mListeners.remove(listener);
+            Registration<TRequest, TListener> oldRegistration = mListeners.remove(listener);
             if (oldRegistration == null) {
                 return;
             }
             oldRegistration.unregister();
 
-            if (mListeners.isEmpty()) {
+            boolean lastRequest = mListeners.isEmpty();
+            TRequest merged = lastRequest ? null : mergeRequests();
+            boolean newRequest = !lastRequest && !Objects.equals(merged, mMergedRequest);
+
+            if (lastRequest || newRequest) {
                 unregisterService();
+                mMergedRequest = merged;
+                if (newRequest) {
+                    registerService(mMergedRequest);
+                }
             }
         }
     }
 
     @SuppressWarnings("unchecked")
-    protected T convertKey(@NonNull Object listener) {
-        return (T) listener;
+    protected TListener convertKey(@NonNull Object listener) {
+        return (TListener) listener;
     }
 
-    protected abstract boolean registerService() throws RemoteException;
+    protected abstract boolean registerService(TRequest request) throws RemoteException;
     protected abstract void unregisterService() throws RemoteException;
 
-    protected void execute(Consumer<T> operation) {
+    @Nullable
+    protected TRequest merge(@NonNull TRequest[] requests) {
+        for (TRequest request : requests) {
+            Preconditions.checkArgument(request == null,
+                    "merge() has to be overridden for non-null requests.");
+        }
+        return null;
+    }
+
+    protected void execute(Consumer<TListener> operation) {
         synchronized (mListeners) {
-            for (Registration<T> registration : mListeners.values()) {
+            for (Registration<TRequest, TListener> registration : mListeners.values()) {
                 registration.execute(operation);
             }
         }
     }
+
+    @GuardedBy("mListeners")
+    @SuppressWarnings("unchecked")
+    @Nullable
+    private TRequest mergeRequests() {
+        Preconditions.checkState(Thread.holdsLock(mListeners));
+
+        if (mListeners.isEmpty()) {
+            return null;
+        }
+
+        if (mListeners.size() == 1) {
+            return mListeners.valueAt(0).getRequest();
+        }
+
+        TRequest[] requests = (TRequest[]) new Object[mListeners.size()];
+        for (int index = 0; index < mListeners.size(); index++) {
+            requests[index] = mListeners.valueAt(index).getRequest();
+        }
+        return merge(requests);
+    }
 }
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 6a5c0ec..a99e68f 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -22,6 +22,7 @@
 import android.location.GeocoderParams;
 import android.location.Geofence;
 import android.location.GnssMeasurementCorrections;
+import android.location.GnssRequest;
 import android.location.IBatchedLocationCallback;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssStatusListener;
@@ -69,8 +70,10 @@
         double upperRightLatitude, double upperRightLongitude, int maxResults,
         in GeocoderParams params, out List<Address> addrs);
 
-    boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener,
-             String packageName, String featureId, String listenerIdentifier);
+    boolean addGnssMeasurementsListener(in GnssRequest request,
+            in IGnssMeasurementsListener listener,
+            String packageName, String featureId,
+            String listenerIdentifier);
     void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections,
             in String packageName);
     long getGnssCapabilities(in String packageName);
@@ -107,6 +110,7 @@
 
     boolean isProviderEnabledForUser(String provider, int userId);
     boolean isLocationEnabledForUser(int userId);
+    void setLocationEnabledForUser(boolean enabled, int userId);
     void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
     void removeTestProvider(String provider, String opPackageName);
     void setTestProviderLocation(String provider, in Location loc, String opPackageName);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 7e6486c..8ae967f 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -478,13 +478,11 @@
     @TestApi
     @RequiresPermission(WRITE_SECURE_SETTINGS)
     public void setLocationEnabledForUser(boolean enabled, @NonNull UserHandle userHandle) {
-        Settings.Secure.putIntForUser(
-                mContext.getContentResolver(),
-                Settings.Secure.LOCATION_MODE,
-                enabled
-                        ? Settings.Secure.LOCATION_MODE_ON
-                        : Settings.Secure.LOCATION_MODE_OFF,
-                userHandle.getIdentifier());
+        try {
+            mService.setLocationEnabledForUser(enabled, userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -2199,7 +2197,7 @@
      * Registers a GNSS Measurement callback.
      *
      * @param request  extra parameters to pass to GNSS measurement provider. For example, if {@link
-     *                 GnssRequest#isFullTrackingEnabled()} is true, GNSS chipset switches off duty
+     *                 GnssRequest#isFullTracking()} is true, GNSS chipset switches off duty
      *                 cycling.
      * @param executor the executor that the callback runs on.
      * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
@@ -2216,7 +2214,12 @@
             @NonNull GnssRequest request,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull GnssMeasurementsEvent.Callback callback) {
-        throw new RuntimeException();
+        Preconditions.checkArgument(request != null, "invalid null request");
+        try {
+            return mGnssMeasurementsListenerManager.addListener(request, callback, executor);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -2763,8 +2766,7 @@
     }
 
     private class GnssStatusListenerManager extends
-            AbstractListenerManager<GnssStatus.Callback> {
-
+            AbstractListenerManager<Void, GnssStatus.Callback> {
         @Nullable
         private IGnssStatusListener mListenerTransport;
 
@@ -2782,19 +2784,19 @@
 
         public boolean addListener(@NonNull GpsStatus.Listener listener, @NonNull Executor executor)
                 throws RemoteException {
-            return addInternal(listener, executor);
+            return addInternal(null, listener, executor);
         }
 
         public boolean addListener(@NonNull OnNmeaMessageListener listener,
                 @NonNull Handler handler)
                 throws RemoteException {
-            return addInternal(listener, handler);
+            return addInternal(null, listener, handler);
         }
 
         public boolean addListener(@NonNull OnNmeaMessageListener listener,
                 @NonNull Executor executor)
                 throws RemoteException {
-            return addInternal(listener, executor);
+            return addInternal(null, listener, executor);
         }
 
         @Override
@@ -2833,7 +2835,7 @@
         }
 
         @Override
-        protected boolean registerService() throws RemoteException {
+        protected boolean registerService(Void ignored) throws RemoteException {
             Preconditions.checkState(mListenerTransport == null);
 
             GnssStatusListener transport = new GnssStatusListener();
@@ -2893,17 +2895,17 @@
     }
 
     private class GnssMeasurementsListenerManager extends
-            AbstractListenerManager<GnssMeasurementsEvent.Callback> {
+            AbstractListenerManager<GnssRequest, GnssMeasurementsEvent.Callback> {
 
         @Nullable
         private IGnssMeasurementsListener mListenerTransport;
 
         @Override
-        protected boolean registerService() throws RemoteException {
+        protected boolean registerService(GnssRequest request) throws RemoteException {
             Preconditions.checkState(mListenerTransport == null);
 
             GnssMeasurementsListener transport = new GnssMeasurementsListener();
-            if (mService.addGnssMeasurementsListener(transport, mContext.getPackageName(),
+            if (mService.addGnssMeasurementsListener(request, transport, mContext.getPackageName(),
                     mContext.getFeatureId(), "gnss measurement callback")) {
                 mListenerTransport = transport;
                 return true;
@@ -2920,6 +2922,18 @@
             mListenerTransport = null;
         }
 
+        @Override
+        @Nullable
+        protected GnssRequest merge(@NonNull GnssRequest[] requests) {
+            Preconditions.checkArgument(requests.length > 0);
+            for (GnssRequest request : requests) {
+                if (request.isFullTracking()) {
+                    return request;
+                }
+            }
+            return requests[0];
+        }
+
         private class GnssMeasurementsListener extends IGnssMeasurementsListener.Stub {
             @Override
             public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) {
@@ -2934,13 +2948,13 @@
     }
 
     private class GnssNavigationMessageListenerManager extends
-            AbstractListenerManager<GnssNavigationMessage.Callback> {
+            AbstractListenerManager<Void, GnssNavigationMessage.Callback> {
 
         @Nullable
         private IGnssNavigationMessageListener mListenerTransport;
 
         @Override
-        protected boolean registerService() throws RemoteException {
+        protected boolean registerService(Void ignored) throws RemoteException {
             Preconditions.checkState(mListenerTransport == null);
 
             GnssNavigationMessageListener transport = new GnssNavigationMessageListener();
@@ -2975,13 +2989,13 @@
     }
 
     private class BatchedLocationCallbackManager extends
-            AbstractListenerManager<BatchedLocationCallback> {
+            AbstractListenerManager<Void, BatchedLocationCallback> {
 
         @Nullable
         private IBatchedLocationCallback mListenerTransport;
 
         @Override
-        protected boolean registerService() throws RemoteException {
+        protected boolean registerService(Void ignored) throws RemoteException {
             Preconditions.checkState(mListenerTransport == null);
 
             BatchedLocationCallback transport = new BatchedLocationCallback();
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 767b67b..d237975 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -1457,6 +1457,9 @@
     private int mRw2JpgFromRawOffset;
     private boolean mIsSupportedFile;
     private boolean mModified;
+    // XMP data can be contained as either part of the EXIF data (tag number 700), or as a
+    // separate data marker (a separate MARKER_APP1).
+    private boolean mXmpIsFromSeparateMarker;
 
     // Pattern to check non zero timestamp
     private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
@@ -2837,10 +2840,12 @@
                         final long offset = start + IDENTIFIER_XMP_APP1.length;
                         final byte[] value = Arrays.copyOfRange(bytes,
                                 IDENTIFIER_XMP_APP1.length, bytes.length);
-
+                        // TODO: check if ignoring separate XMP data when tag 700 already exists is
+                        //  valid.
                         if (getAttribute(TAG_XMP) == null) {
                             mAttributes[IFD_TYPE_PRIMARY].put(TAG_XMP, new ExifAttribute(
                                     IFD_FORMAT_BYTE, value.length, offset, value));
+                            mXmpIsFromSeparateMarker = true;
                         }
                     }
                     break;
@@ -3445,11 +3450,24 @@
         }
         dataOutputStream.writeByte(MARKER_SOI);
 
+        // Remove XMP data if it is from a separate marker (IDENTIFIER_XMP_APP1, not
+        // IDENTIFIER_EXIF_APP1)
+        // Will re-add it later after the rest of the file is written
+        ExifAttribute xmpAttribute = null;
+        if (getAttribute(TAG_XMP) != null && mXmpIsFromSeparateMarker) {
+            xmpAttribute = (ExifAttribute) mAttributes[IFD_TYPE_PRIMARY].remove(TAG_XMP);
+        }
+
         // Write EXIF APP1 segment
         dataOutputStream.writeByte(MARKER);
         dataOutputStream.writeByte(MARKER_APP1);
         writeExifSegment(dataOutputStream);
 
+        // Re-add previously removed XMP data.
+        if (xmpAttribute != null) {
+            mAttributes[IFD_TYPE_PRIMARY].put(TAG_XMP, xmpAttribute);
+        }
+
         byte[] bytes = new byte[4096];
 
         while (true) {
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index b1f930d..2c1fdab 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -247,13 +247,23 @@
         Objects.requireNonNull(packageName, "packageName must not be null");
         Objects.requireNonNull(route, "route must not be null");
 
+        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 (controller.getSessionInfo().getTransferrableRoutes().contains(route.getId())) {
+            if (!transferred && controller.getSessionInfo().getTransferrableRoutes()
+                    .contains(route.getId())) {
                 controller.transferToRoute(route);
-                return;
+                transferred = true;
+            } else if (!controller.getSessionInfo().isSystemSession()) {
+                controller.release();
             }
         }
 
+        if (transferred) {
+            return;
+        }
+
         Client client;
         synchronized (sLock) {
             client = mClient;
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 870c1b4..486c0c2 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -266,8 +266,12 @@
      * playback after the session has been stopped. If your app is started in
      * this way an {@link Intent#ACTION_MEDIA_BUTTON} intent will be sent via
      * the pending intent.
+     * <p>
+     * The pending intent is recommended to be explicit to follow the security recommendation of
+     * {@link PendingIntent#getActivity}.
      *
      * @param mbr The {@link PendingIntent} to send the media button event to.
+     * @see PendingIntent#getActivity
      */
     public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
         try {
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 3561f83..9b183a3 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -244,7 +244,7 @@
      *
      * <p>
      * Tuner events are started when {@link #tune(FrontendSettings)} is called and end when {@link
-     * #stopTune()} is called.
+     * #cancelTuning()} is called.
      *
      * @param eventListener receives tune events.
      * @throws SecurityException if the caller does not have appropriate permissions.
@@ -309,7 +309,7 @@
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @Result
-    public int stopTune() {
+    public int cancelTuning() {
         TunerUtils.checkTunerPermission(mContext);
         return nativeStopTune();
     }
@@ -322,8 +322,8 @@
      * @param settings A {@link FrontendSettings} to configure the frontend.
      * @param scanType The scan type.
      * @throws SecurityException     if the caller does not have appropriate permissions.
-     * @throws IllegalStateException if {@code scan} is called again before {@link #stopScan()} is
-     *                               called.
+     * @throws IllegalStateException if {@code scan} is called again before
+     *                               {@link #cancelScanning()} is called.
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @Result
@@ -354,7 +354,7 @@
      */
     @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
     @Result
-    public int stopScan() {
+    public int cancelScanning() {
         TunerUtils.checkTunerPermission(mContext);
         int retVal = nativeStopScan();
         mScanCallback = null;
diff --git a/media/tests/TunerTest/OWNERS b/media/tests/TunerTest/OWNERS
new file mode 100644
index 0000000..73ea663
--- /dev/null
+++ b/media/tests/TunerTest/OWNERS
@@ -0,0 +1,4 @@
+amyjojo@google.com
+nchalko@google.com
+quxiangfang@google.com
+shubang@google.com
diff --git a/packages/Incremental/NativeAdbDataLoader/src/com/android/incremental/nativeadb/NativeAdbDataLoaderService.java b/packages/Incremental/NativeAdbDataLoader/src/com/android/incremental/nativeadb/NativeAdbDataLoaderService.java
index bd5b7959..c4e41c8 100644
--- a/packages/Incremental/NativeAdbDataLoader/src/com/android/incremental/nativeadb/NativeAdbDataLoaderService.java
+++ b/packages/Incremental/NativeAdbDataLoader/src/com/android/incremental/nativeadb/NativeAdbDataLoaderService.java
@@ -16,6 +16,8 @@
 
 package com.android.incremental.nativeadb;
 
+import android.annotation.NonNull;
+import android.content.pm.DataLoaderParams;
 import android.service.dataloader.DataLoaderService;
 
 /** This code is used for testing only. */
@@ -26,7 +28,7 @@
     }
 
     @Override
-    public DataLoader onCreateDataLoader() {
+    public DataLoader onCreateDataLoader(@NonNull DataLoaderParams dataLoaderParams) {
         return null;
     }
 }
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 4d96251..804e0cb 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -553,6 +553,60 @@
     <string name="enable_adb_summary">Debug mode when USB is connected</string>
     <!-- Setting title to revoke secure USB debugging authorizations -->
     <string name="clear_adb_keys">Revoke USB debugging authorizations</string>
+    <!-- [CHAR LIMIT=32] Setting title for ADB wireless switch -->
+    <string name="enable_adb_wireless">Wireless debugging</string>
+    <!-- [CHAR LIMIT=NONE] Setting checkbox summary for whether to enable Wireless debugging support on the phone -->
+    <string name="enable_adb_wireless_summary">Debug mode when Wi\u2011Fi is connected</string>
+    <!-- [CHAR LIMIT=32] Summary text when ADB wireless has error -->
+    <string name="adb_wireless_error">Error</string>
+    <!-- [CHAR LIMIT=32] Setting title for ADB wireless fragment -->
+    <string name="adb_wireless_settings">Wireless debugging</string>
+    <!-- [CHAR LIMIT=NONE] Wireless debugging settings. text displayed when wireless debugging is off and network list is empty. -->
+    <string name="adb_wireless_list_empty_off">To see and use available devices, turn on wireless debugging</string>
+    <!-- [CHAR LIMIT=50] Title for adb wireless pair by QR code preference -->
+    <string name="adb_pair_method_qrcode_title">Pair device with QR code</string>
+    <!-- [CHAR LIMIT=NONE] Summary for adb wireless pair by QR code preference -->
+    <string name="adb_pair_method_qrcode_summary">Pair new devices using QR code Scanner</string>
+    <!-- [CHAR LIMIT=50] Title for adb wireless pair by pairing code preference -->
+    <string name="adb_pair_method_code_title">Pair device with pairing code</string>
+    <!-- [CHAR LIMIT=NONE] Summary for adb wireless pair by pairing code preference -->
+    <string name="adb_pair_method_code_summary">Pair new devices using six digit code</string>
+    <!-- [CHAR LIMIT=50] Title for adb wireless paired devices category -->
+    <string name="adb_paired_devices_title">Paired devices</string>
+    <!-- [CHAR LIMIT=50] Summary for adb wireless paired device preference -->
+    <string name="adb_wireless_device_connected_summary">Currently connected</string>
+    <!-- [CHAR LIMIT=50] Title for the adb device details fragment -->
+    <string name="adb_wireless_device_details_title">Device details</string>
+    <!-- [CHAR LIMIT=16] Button label to forget an adb device -->
+    <string name="adb_device_forget">Forget</string>
+    <!-- [CHAR LIMIT=50] Title format for mac address preference in adb device details fragment -->
+    <string name="adb_device_fingerprint_title_format">Device fingerprint: <xliff:g id="fingerprint_param" example="a1:b2:c3:d4:e5:f6">%1$s</xliff:g></string>
+    <!-- [CHAR LIMIT=50] Title for adb wireless connection failed dialog -->
+    <string name="adb_wireless_connection_failed_title">Connection unsuccessful</string>
+    <!-- [CHAR LIMIT=NONE] Message for adb wireless connection failed dialog -->
+    <string name="adb_wireless_connection_failed_message">Make sure <xliff:g id="device_name" example="Bob's Macbook">%1$s</xliff:g> is connected to the correct network</string>
+    <!-- [CHAR LIMIT=32] Adb wireless pairing device dialog title -->
+    <string name="adb_pairing_device_dialog_title">Pair with device</string>
+    <!-- [CHAR LIMIT=32] Adb wireless pairing device dialog pairing code label -->
+    <string name="adb_pairing_device_dialog_pairing_code_label">Wi\u2011Fi pairing code</string>
+    <!-- [CHAR LIMIT=50] Adb Wireless pairing device failed dialog title -->
+    <string name="adb_pairing_device_dialog_failed_title">Pairing unsuccessful</string>
+    <!-- [CHAR LIMIT=NONE] Adb wireless pairing device failed dialog message -->
+    <string name="adb_pairing_device_dialog_failed_msg">Make sure the device is connected to the same network.</string>
+    <!-- [CHAR LIMIT=NONE] Adb wireless qr code scanner description -->
+    <string name="adb_wireless_qrcode_summary">Pair device over Wi\u2011Fi by scanning a QR code</string>
+    <!-- [CHAR LIMIT=NONE] Adb wireless QR code pairing in progress text -->
+    <string name="adb_wireless_verifying_qrcode_text">Pairing device\u2026</string>
+    <!-- [CHAR LIMIT=NONE] Adb wireless QR code failed message -->
+    <string name="adb_qrcode_pairing_device_failed_msg">Failed to pair the device. Either the QR code was incorrect, or the device is not connected to the same network.</string>
+    <!-- [CHAR LIMIT=50] Adb Wireless ip address and port title -->
+    <string name="adb_wireless_ip_addr_preference_title">IP address \u0026 Port</string>
+    <!-- [CHAR LIMIT=NONE] Adb Wireless QR code pairing scanner title -->
+    <string name="adb_wireless_qrcode_pairing_title">Scan QR code</string>
+    <!-- [CHAR LIMIT=NONE] Adb Wireless QR code pairing description -->
+    <string name="adb_wireless_qrcode_pairing_description">Pair device over Wi\u2011Fi by scanning a QR Code</string>
+    <!--Adb wireless search Keywords [CHAR LIMIT=NONE]-->
+    <string name="keywords_adb_wireless">adb, debug, dev</string>
     <!-- [CHAR LIMIT=NONE] Setting checkbox title for Whether to include bug report item in power menu. -->
     <string name="bugreport_in_power">Bug report shortcut</string>
     <!-- [CHAR LIMIT=NONE] Setting checkbox summary for Whether to include bug report item in power -->
@@ -689,6 +743,10 @@
     <string name="adb_warning_title">Allow USB debugging?</string>
     <!-- Warning text to user about the implications of enabling USB debugging -->
     <string name="adb_warning_message">USB debugging is intended for development purposes only. Use it to copy data between your computer and your device, install apps on your device without notification, and read log data.</string>
+    <!-- Title of warning dialog about the implications of enabling USB debugging [CHAR LIMIT=NONE] -->
+    <string name="adbwifi_warning_title">Allow wireless debugging?</string>
+    <!-- Warning text to user about the implications of enabling USB debugging [CHAR LIMIT=NONE] -->
+    <string name="adbwifi_warning_message">Wireless debugging is intended for development purposes only. Use it to copy data between your computer and your device, install apps on your device without notification, and read log data.</string>
     <!-- Message of dialog confirming that user wants to revoke access to adb from all computers they have authorized -->
     <string name="adb_keys_warning_message">Revoke access to USB debugging from all computers you\u2019ve previously authorized?</string>
     <!-- Title of warning dialog about the implications of enabling developer settings -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index b9081f2..b725ba5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -16,12 +16,9 @@
 package com.android.settingslib.media;
 
 import android.content.Context;
-import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
-import android.text.TextUtils;
-import android.util.Log;
 
 import com.android.settingslib.R;
 import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -62,46 +59,6 @@
         return MediaDeviceUtils.getId(mRouteInfo);
     }
 
-    @Override
-    public void requestSetVolume(int volume) {
-        mRouterManager.requestSetVolume(mRouteInfo, volume);
-    }
-
-    @Override
-    public int getMaxVolume() {
-        return mRouteInfo.getVolumeMax();
-    }
-
-    @Override
-    public int getCurrentVolume() {
-        return mRouteInfo.getVolume();
-    }
-
-    @Override
-    public String getClientPackageName() {
-        return mRouteInfo.getClientPackageName();
-    }
-
-    @Override
-    public String getClientAppLabel() {
-        final String packageName = mRouteInfo.getClientPackageName();
-        if (TextUtils.isEmpty(packageName)) {
-            Log.d(TAG, "Client package name is empty");
-            return mContext.getResources().getString(R.string.unknown);
-        }
-        try {
-            final PackageManager packageManager = mContext.getPackageManager();
-            final String appLabel = packageManager.getApplicationLabel(
-                    packageManager.getApplicationInfo(packageName, 0)).toString();
-            if (!TextUtils.isEmpty(appLabel)) {
-                return appLabel;
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "unable to find " + packageName);
-        }
-        return mContext.getResources().getString(R.string.unknown);
-    }
-
     public boolean isConnected() {
         return true;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 580e086..33c3d7e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -16,14 +16,18 @@
 package com.android.settingslib.media;
 
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
 import android.text.TextUtils;
+import android.util.Log;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.VisibleForTesting;
 
+import com.android.settingslib.R;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -113,7 +117,9 @@
      *
      * @param volume is the new value.
      */
+
     public void requestSetVolume(int volume) {
+        mRouterManager.requestSetVolume(mRouteInfo, volume);
     }
 
     /**
@@ -122,7 +128,7 @@
      * @return max volume.
      */
     public int getMaxVolume() {
-        return 100;
+        return mRouteInfo.getVolumeMax();
     }
 
     /**
@@ -131,7 +137,7 @@
      * @return current volume.
      */
     public int getCurrentVolume() {
-        return 0;
+        return mRouteInfo.getVolume();
     }
 
     /**
@@ -140,7 +146,7 @@
      * @return package name.
      */
     public String getClientPackageName() {
-        return null;
+        return mRouteInfo.getClientPackageName();
     }
 
     /**
@@ -149,7 +155,22 @@
      * @return application label.
      */
     public String getClientAppLabel() {
-        return null;
+        final String packageName = mRouteInfo.getClientPackageName();
+        if (TextUtils.isEmpty(packageName)) {
+            Log.d(TAG, "Client package name is empty");
+            return mContext.getResources().getString(R.string.unknown);
+        }
+        try {
+            final PackageManager packageManager = mContext.getPackageManager();
+            final String appLabel = packageManager.getApplicationLabel(
+                    packageManager.getApplicationInfo(packageName, 0)).toString();
+            if (!TextUtils.isEmpty(appLabel)) {
+                return appLabel;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "unable to find " + packageName);
+        }
+        return mContext.getResources().getString(R.string.unknown);
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index bfb79c0..954eb9b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -181,6 +181,7 @@
     static final String KEY_SCANRESULTS = "key_scanresults";
     static final String KEY_SCOREDNETWORKCACHE = "key_scorednetworkcache";
     static final String KEY_CONFIG = "key_config";
+    static final String KEY_PASSPOINT_UNIQUE_ID = "key_passpoint_unique_id";
     static final String KEY_FQDN = "key_fqdn";
     static final String KEY_PROVIDER_FRIENDLY_NAME = "key_provider_friendly_name";
     static final String KEY_EAPTYPE = "eap_psktype";
@@ -217,7 +218,7 @@
     public static final int UNREACHABLE_RSSI = Integer.MIN_VALUE;
 
     public static final String KEY_PREFIX_AP = "AP:";
-    public static final String KEY_PREFIX_FQDN = "FQDN:";
+    public static final String KEY_PREFIX_PASSPOINT_UNIQUE_ID = "PASSPOINT:";
     public static final String KEY_PREFIX_OSU = "OSU:";
 
     private final Context mContext;
@@ -250,6 +251,7 @@
      * Information associated with the {@link PasspointConfiguration}.  Only maintaining
      * the relevant info to preserve spaces.
      */
+    private String mPasspointUniqueId;
     private String mFqdn;
     private String mProviderFriendlyName;
     private boolean mIsRoaming = false;
@@ -308,6 +310,9 @@
                 mScoredNetworkCache.put(timedScore.getScore().networkKey.wifiKey.bssid, timedScore);
             }
         }
+        if (savedState.containsKey(KEY_PASSPOINT_UNIQUE_ID)) {
+            mPasspointUniqueId = savedState.getString(KEY_PASSPOINT_UNIQUE_ID);
+        }
         if (savedState.containsKey(KEY_FQDN)) {
             mFqdn = savedState.getString(KEY_FQDN);
         }
@@ -351,6 +356,7 @@
      */
     public AccessPoint(Context context, PasspointConfiguration config) {
         mContext = context;
+        mPasspointUniqueId = config.getUniqueId();
         mFqdn = config.getHomeSp().getFqdn();
         mProviderFriendlyName = config.getHomeSp().getFriendlyName();
         mSubscriptionExpirationTimeInMillis = config.getSubscriptionExpirationTimeInMillis();
@@ -371,6 +377,7 @@
         mContext = context;
         networkId = config.networkId;
         mConfig = config;
+        mPasspointUniqueId = config.getKey();
         mFqdn = config.FQDN;
         setScanResultsPasspoint(homeScans, roamingScans);
         updateKey();
@@ -407,7 +414,7 @@
         if (isPasspoint()) {
             mKey = getKey(mConfig);
         } else if (isPasspointConfig()) {
-            mKey = getKey(mFqdn);
+            mKey = getKey(mPasspointUniqueId);
         } else if (isOsuProvider()) {
             mKey = getKey(mOsuProvider);
         } else { // Non-Passpoint AP
@@ -677,19 +684,19 @@
      */
     public static String getKey(WifiConfiguration config) {
         if (config.isPasspoint()) {
-            return getKey(config.FQDN);
+            return getKey(config.getKey());
         } else {
             return getKey(removeDoubleQuotes(config.SSID), config.BSSID, getSecurity(config));
         }
     }
 
     /**
-     * Returns the AccessPoint key corresponding to a Passpoint network by its FQDN.
+     * Returns the AccessPoint key corresponding to a Passpoint network by its unique identifier.
      */
-    public static String getKey(String fqdn) {
+    public static String getKey(String passpointUniqueId) {
         return new StringBuilder()
-                .append(KEY_PREFIX_FQDN)
-                .append(fqdn).toString();
+                .append(KEY_PREFIX_PASSPOINT_UNIQUE_ID)
+                .append(passpointUniqueId).toString();
     }
 
     /**
@@ -766,7 +773,7 @@
 
     public boolean matches(WifiConfiguration config) {
         if (config.isPasspoint()) {
-            return (isPasspoint() && config.FQDN.equals(mConfig.FQDN));
+            return (isPasspoint() && config.getKey().equals(mConfig.getKey()));
         }
 
         if (!ssid.equals(removeDoubleQuotes(config.SSID))
@@ -1052,7 +1059,7 @@
     public String getConfigName() {
         if (mConfig != null && mConfig.isPasspoint()) {
             return mConfig.providerFriendlyName;
-        } else if (mFqdn != null) {
+        } else if (mPasspointUniqueId != null) {
             return mProviderFriendlyName;
         } else {
             return ssid;
@@ -1254,7 +1261,7 @@
      * Return true if this AccessPoint represents a Passpoint provider configuration.
      */
     public boolean isPasspointConfig() {
-        return mFqdn != null && mConfig == null;
+        return mPasspointUniqueId != null && mConfig == null;
     }
 
     /**
@@ -1310,8 +1317,12 @@
         if (info.isOsuAp() || mOsuStatus != null) {
             return (info.isOsuAp() && mOsuStatus != null);
         } else if (info.isPasspointAp() || isPasspoint()) {
+            // TODO: Use TextUtils.equals(info.getPasspointUniqueId(), mConfig.getKey()) when API
+            //  is available
             return (info.isPasspointAp() && isPasspoint()
-                    && TextUtils.equals(info.getPasspointFqdn(), mConfig.FQDN));
+                    && TextUtils.equals(info.getPasspointFqdn(), mConfig.FQDN)
+                    && TextUtils.equals(info.getPasspointProviderFriendlyName(),
+                    mConfig.providerFriendlyName));
         }
 
         if (networkId != WifiConfiguration.INVALID_NETWORK_ID) {
@@ -1377,6 +1388,9 @@
         if (mNetworkInfo != null) {
             savedState.putParcelable(KEY_NETWORKINFO, mNetworkInfo);
         }
+        if (mPasspointUniqueId != null) {
+            savedState.putString(KEY_PASSPOINT_UNIQUE_ID, mPasspointUniqueId);
+        }
         if (mFqdn != null) {
             savedState.putString(KEY_FQDN, mFqdn);
         }
@@ -1949,11 +1963,11 @@
                 return;
             }
 
-            String fqdn = passpointConfig.getHomeSp().getFqdn();
+            String uniqueId = passpointConfig.getUniqueId();
             for (Pair<WifiConfiguration, Map<Integer, List<ScanResult>>> pairing :
                     wifiManager.getAllMatchingWifiConfigs(wifiManager.getScanResults())) {
                 WifiConfiguration config = pairing.first;
-                if (TextUtils.equals(config.FQDN, fqdn)) {
+                if (TextUtils.equals(config.getKey(), uniqueId)) {
                     List<ScanResult> homeScans =
                             pairing.second.get(WifiManager.PASSPOINT_HOME_NETWORK);
                     List<ScanResult> roamingScans =
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
index f21e466..2fb2481 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
@@ -84,7 +84,7 @@
         bundle.putParcelable(AccessPoint.KEY_NETWORKINFO, mNetworkInfo);
         bundle.putParcelable(AccessPoint.KEY_WIFIINFO, mWifiInfo);
         if (mFqdn != null) {
-            bundle.putString(AccessPoint.KEY_FQDN, mFqdn);
+            bundle.putString(AccessPoint.KEY_PASSPOINT_UNIQUE_ID, mFqdn);
         }
         if (mProviderFriendlyName != null) {
             bundle.putString(AccessPoint.KEY_PROVIDER_FRIENDLY_NAME, mProviderFriendlyName);
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 26abf71..586c154 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -606,7 +606,7 @@
 
             List<ScanResult> cachedScanResults = new ArrayList<>(mScanResultCache.values());
 
-            // Add a unique Passpoint AccessPoint for each Passpoint profile's FQDN.
+            // Add a unique Passpoint AccessPoint for each Passpoint profile's unique identifier.
             accessPoints.addAll(updatePasspointAccessPoints(
                     mWifiManager.getAllMatchingWifiConfigs(cachedScanResults), cachedAccessPoints));
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
index 04ceb21..77a67c2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -21,9 +21,6 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageStats;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
 
@@ -36,14 +33,11 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
-import org.robolectric.shadows.ShadowPackageManager;
 
 @RunWith(RobolectricTestRunner.class)
 public class InfoMediaDeviceTest {
 
     private static final String TEST_PACKAGE_NAME = "com.test.packagename";
-    private static final String TEST_PACKAGE_NAME2 = "com.test.packagename2";
     private static final String TEST_ID = "test_id";
     private static final String TEST_NAME = "test_name";
 
@@ -52,27 +46,13 @@
     @Mock
     private MediaRoute2Info mRouteInfo;
 
-
     private Context mContext;
     private InfoMediaDevice mInfoMediaDevice;
-    private ShadowPackageManager mShadowPackageManager;
-    private ApplicationInfo mAppInfo;
-    private PackageInfo mPackageInfo;
-    private PackageStats mPackageStats;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
-        mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
-        mAppInfo = new ApplicationInfo();
-        mAppInfo.flags = ApplicationInfo.FLAG_INSTALLED;
-        mAppInfo.packageName = TEST_PACKAGE_NAME;
-        mAppInfo.name = TEST_NAME;
-        mPackageInfo = new PackageInfo();
-        mPackageInfo.packageName = TEST_PACKAGE_NAME;
-        mPackageInfo.applicationInfo = mAppInfo;
-        mPackageStats = new PackageStats(TEST_PACKAGE_NAME);
 
         mInfoMediaDevice = new InfoMediaDevice(mContext, mRouterManager, mRouteInfo,
                 TEST_PACKAGE_NAME);
@@ -106,32 +86,4 @@
 
         assertThat(mInfoMediaDevice.getId()).isEqualTo(TEST_ID);
     }
-
-    @Test
-    public void getClientPackageName_returnPackageName() {
-        when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
-
-        assertThat(mInfoMediaDevice.getClientPackageName()).isEqualTo(TEST_PACKAGE_NAME);
-    }
-
-    @Test
-    public void getClientAppLabel_matchedPackageName_returnLabel() {
-        when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
-
-        assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(
-                mContext.getResources().getString(R.string.unknown));
-
-        mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
-
-        assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(TEST_NAME);
-    }
-
-    @Test
-    public void getClientAppLabel_noMatchedPackageName_returnDefault() {
-        mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
-        when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME2);
-
-        assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(
-                mContext.getResources().getString(R.string.unknown));
-    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index fb8b78b..3f29b72 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -23,9 +23,13 @@
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageStats;
 import android.media.MediaRoute2Info;
 import android.media.MediaRouter2Manager;
 
+import com.android.settingslib.R;
 import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.HearingAidProfile;
@@ -39,6 +43,8 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowPackageManager;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -58,6 +64,8 @@
     private static final String ROUTER_ID_2 = "RouterId_2";
     private static final String ROUTER_ID_3 = "RouterId_3";
     private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
+    private static final String TEST_PACKAGE_NAME2 = "com.test.playmusic2";
+    private static final String TEST_APPLICATION_LABEL = "playmusic";
     private final BluetoothClass mHeadreeClass =
             new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
     private final BluetoothClass mCarkitClass =
@@ -111,6 +119,10 @@
     private InfoMediaDevice mInfoMediaDevice3;
     private List<MediaDevice> mMediaDevices = new ArrayList<>();
     private PhoneMediaDevice mPhoneMediaDevice;
+    private ShadowPackageManager mShadowPackageManager;
+    private ApplicationInfo mAppInfo;
+    private PackageInfo mPackageInfo;
+    private PackageStats mPackageStats;
 
     @Before
     public void setUp() {
@@ -394,4 +406,46 @@
 
         verify(mMediaRouter2Manager).selectRoute(TEST_PACKAGE_NAME, mRouteInfo1);
     }
+
+    @Test
+    public void getClientPackageName_returnPackageName() {
+        when(mRouteInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        assertThat(mInfoMediaDevice1.getClientPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+    }
+
+    private void initPackage() {
+        mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
+        mAppInfo = new ApplicationInfo();
+        mAppInfo.flags = ApplicationInfo.FLAG_INSTALLED;
+        mAppInfo.packageName = TEST_PACKAGE_NAME;
+        mAppInfo.name = TEST_APPLICATION_LABEL;
+        mPackageInfo = new PackageInfo();
+        mPackageInfo.packageName = TEST_PACKAGE_NAME;
+        mPackageInfo.applicationInfo = mAppInfo;
+        mPackageStats = new PackageStats(TEST_PACKAGE_NAME);
+    }
+
+    @Test
+    public void getClientAppLabel_matchedPackageName_returnLabel() {
+        initPackage();
+        when(mRouteInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(
+                mContext.getResources().getString(R.string.unknown));
+
+        mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
+
+        assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(TEST_APPLICATION_LABEL);
+    }
+
+    @Test
+    public void getClientAppLabel_noMatchedPackageName_returnDefault() {
+        initPackage();
+        mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
+        when(mRouteInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME2);
+
+        assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(
+                mContext.getResources().getString(R.string.unknown));
+    }
 }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 1fe967b..5458676e 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -646,16 +646,17 @@
                   android:label="Controls Providers"
                   android:theme="@style/Theme.ControlsManagement"
                   android:showForAllUsers="true"
+                  android:clearTaskOnLaunch="true"
                   android:excludeFromRecents="true"
                   android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
                   android:visibleToInstantApps="true">
         </activity>
 
         <activity android:name=".controls.management.ControlsFavoritingActivity"
-                  android:parentActivityName=".controls.management.ControlsProviderSelectorActivity"
                   android:theme="@style/Theme.ControlsManagement"
                   android:excludeFromRecents="true"
                   android:showForAllUsers="true"
+                  android:finishOnTaskLaunch="true"
                   android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
                   android:visibleToInstantApps="true">
         </activity>
diff --git a/packages/SystemUI/docs/QS-QQS.png b/packages/SystemUI/docs/QS-QQS.png
new file mode 100644
index 0000000..02de479
--- /dev/null
+++ b/packages/SystemUI/docs/QS-QQS.png
Binary files differ
diff --git a/packages/SystemUI/docs/qs-tiles.md b/packages/SystemUI/docs/qs-tiles.md
new file mode 100644
index 0000000..b48ba67
--- /dev/null
+++ b/packages/SystemUI/docs/qs-tiles.md
@@ -0,0 +1,377 @@
+# Quick Settings Tiles (almost all there is to know about them)
+
+[TOC]
+
+## About this document
+
+This document is a more or less comprehensive summary of the state and infrastructure used by Quick Settings tiles. It provides descriptions about the lifecycle of a tile, how to create new tiles and how SystemUI manages and displays tiles, among other topics.
+
+## What are Quick Settings Tiles?
+
+Quick Settings (from now on, QS) is the expanded panel that contains shortcuts for the user to toggle many settings. This is opened by expanding the notification drawer twice (or once when phone is locked). Quick Quick Settings (QQS) is the smaller panel that appears on top of the notifications before expanding twice and contains some of the toggles with no text.
+
+Each of these toggles that appear either in QS or QQS are called Quick Settings Tiles (or tiles for short). They allow the user to enable or disable settings quickly and sometimes provides access to more comprehensive settings pages.
+
+The following image shows QQS on the left and QS on the right, with the tiles highlighted.
+
+![QQS on the left, QS on the right](QS-QQS.png)
+
+QS Tiles usually depend on one or more Controllers that bind the tile with the necessary service. Controllers are obtained by the backend and used for communication between the user and the device. 
+
+### A note on multi-user support
+
+All the classes described in this document that live inside SystemUI are only instantiated in the process of user 0. The different controllers that back the QS Tiles (also instantiated just in user 0) are user aware and provide an illusion of different instances for different users.
+
+For an example on this, see [`RotationLockController`](/packages/SystemUI/src/com/android/systemui/statusbar/policy/RotationLockControllerImpl.java). This controller for the `RotationLockTile` listens to changes in all users.
+
+## What are tiles made of?
+
+### Tile backend
+
+QS Tiles are composed of the following backend classes.
+
+* [`QSTile`](/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java): Interface providing common behavior for all Tiles. This class also contains some useful utility classes needed for the tiles.
+  * `Icon`: Defines the basic interface for an icon as used by the tiles.
+  * `State`: Encapsulates the state of the Tile in order to communicate between the backend and the UI.
+* [`QSTileImpl`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java): Abstract implementation of `QSTile`, providing basic common behavior for all tiles. Also implements extensions for different types of `Icon`. All tiles currently defined in SystemUI subclass from this implementation.
+* [`SystemUI/src/com/android/systemui/qs/tiles`](/packages/SystemUI/src/com/android/systemui/qs/tiles): Each tile from SystemUI is defined here by a class that extends `QSTileImpl`. These implementations connect to corresponding controllers. The controllers serve two purposes:
+    * track the state of the device and notify the tile when a change has occurred (for example, bluetooth connected to a device)
+    * accept actions from the tiles to modify the state of the phone (for example, enablind and disabling wifi).
+* [`CustomTile`](/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java): Equivalent to the tiles in the previous item, but used for 3rd party tiles. In depth information to be found in [`CustomTile`](#customtile)
+
+All the elements in SystemUI that work with tiles operate on `QSTile` or the interfaces defined in it. However, all the current implementations of tiles in SystemUI subclass from `QSTileImpl`, as it takes care of many common situations. Throughout this document, we will focus on `QSTileImpl` as examples of tiles.
+
+The interfaces in `QSTile` as well as other interfaces described in this document can be used to implement plugins to add additional tiles or different behavior. For more information, see [plugins.md](plugins.md)
+
+#### Tile State
+
+Each tile has an associated `State` object that is used to communicate information to the corresponding view. The base class `State` has (among others) the following fields:
+
+* **`state`**: one of `Tile#STATE_UNAVAILABLE`, `Tile#STATE_ACTIVE`, `Tile#STATE_INACTIVE`.
+* **`icon`**; icon to display. It may depend on the current state.
+* **`label`**: usually the name of the tile.
+* **`secondaryLabel`**: text to display in a second line. Usually extra state information.
+* **`contentDescription`**
+* **`expandedAccessibilityClassName`**: usually `Switch.class.getName()` for boolean Tiles. This will make screen readers read the current state of the tile as well as the new state when it's toggled. For this, the Tile has to use `BooleanState`.
+* **`handlesLongClick`**: whether the Tile will handle long click. If it won't, it should be set to `false` so it will not be announced for accessibility.
+
+Setting any of these fields during `QSTileImpl#handleUpdateState` will update the UI after it.
+
+Additionally. `BooleanState` has a `value` boolean field that usually would be set to `state == Tile#STATE_ACTIVE`. This is used by accessibility services along with `expandedAccessibilityClassName`.
+
+#### SystemUI tiles
+
+Each tile defined in SystemUI extends `QSTileImpl`. This abstract class implements some common functions and leaves others to be implemented by each tile, in particular those that determine how to handle different events (refresh, click, etc.).
+
+For more information on how to implement a tile in SystemUI, see [Implementing a SystemUI tile](#implementing-a-systemui-tile).
+
+### Tile views
+
+Each Tile has a couple of associated views for displaying it in QS and QQS. These views are updated after the backend updates the `State` using `QSTileImpl#handleUpdateState`.
+
+* **[`com.android.systemui.plugins.qs.QSTileView`](/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java)**: Abstract class that provides basic Tile functionality. These allows external [Factories](#qsfactory) to create Tiles.
+* **[`QSTileBaseView`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java)**: Implementation of `QSTileView` used in QQS that takes care of most of the features of the view:
+  * Holding the icon
+  * Background color and shape
+  * Ripple
+  * Click listening
+* **[`QSTileView`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java)**: Extends `QSTileBaseView`to add label support. Used in QS.
+* **[`QSIconView`](/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSIconView.java)**
+* **[`QSIconViewImpl`](/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java)**
+
+#### QSIconView and QSIconViewImpl
+
+`QSIconView` is an interface that define the basic actions that icons have to respond to. Its base implementation in SystemUI is `QSIconViewImpl` and it and its subclasses are used by all QS tiles.
+
+This `ViewGroup` is a container for the icon used in each tile. It has methods to apply the current `State` of the tile, modifying the icon (color and animations). Classes that inherit from this can add other details that are modified when the `State` changes.
+
+Each `QSTileImpl` can specify that they use a particular implementation of this class when creating an icon.
+
+### How are the backend and the views related?
+
+The backend of the tiles (all the implementations of `QSTileImpl`) communicate with the views by using a `State`. The backend populates the state, and then the view maps the state to a visual representation.
+
+It's important to notice that the state of the tile (internal or visual) is not directly modified by a user action like clicking on the tile. Instead, acting on a tile produces internal state changes on the device, and those trigger the changes on the tile state and UI.
+
+When a container for tiles (`QuickQSPanel` or `QSPanel`) has to display tiles, they create a [`TileRecord`](/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java). This associates the corresponding `QSTile` with its `QSTileView`, doing the following:
+
+* Create the corresponding `QSTileView` to display in that container.
+* Create a callback for `QSTile` to call when its state changes. Note that a single tile will normally have up to two callbacks: one for QS and one for QQS.
+
+#### Life of a tile click
+
+This is a brief run-down of what happens when a user clicks on a tile. Internal changes on the device (for example, changes from Settings) will trigger this process starting in step 3. Throughout this section, we assume that we are dealing with a `QSTileImpl`.
+
+1. User clicks on tile. The following calls happen in sequence:
+   1. `QSTileBaseView#onClickListener`.
+   2. `QSTile#click`.
+   3. `QSTileImpl#handleClick`. This last call sets the new state for the device by using the associated controller.
+2. State in the device changes. This is normally outside of SystemUI's control.
+3. Controller receives a callback (or `Intent`) indicating the change in the device. The following calls happen:
+   1. `QSTileImpl#refreshState`, maybe passing an object with necessary information regarding the new state.
+   2. `QSTileImpl#handleRefreshState`
+4. `QSTileImpl#handleUpdateState` is called to update the state with the new information. This information can be obtained both from the `Object` passed to `refreshState` as well as from the controller.
+5. If the state has changed (in at least one element), `QSTileImpl#handleStateChanged` is called. This will trigger a call to all the associated `QSTile.Callback#onStateChanged`, passing the new `State`.
+6. `QSTileView#onStateChanged` is called and this calls `QSTileView#handleStateChanged`. This method maps the state into the view:
+   * The tile is rippled and the color changes to match the new state.
+   * `QSIconView.setIcon` is called to apply the correct state to the icon and the correct icon to the view.
+   * If the tile is a `QSTileView` (in expanded QS), the labels are changed.
+
+## Third party tiles (TileService)
+
+A third party tile is any Quick Settings tile that is provided by an app (that's not SystemUI). This is implemented by developers subclassing [`TileService`](/core/java/android/service/quicksettings/TileService.java) and interacting with its API.
+
+### API classes
+
+The classes that define the public API are in [core/java/android/service/quicksettings](core/java/android/service/quicksettings).
+
+#### Tile
+
+Parcelable class used to communicate information about the state between the external app and SystemUI. The class supports the following fields:
+
+* Label
+* Subtitle
+* Icon
+* State (`Tile#STATE_ACTIVE`, `Tile#STATE_INACTIVE`, `Tile#STATE_UNAVAILABLE`)
+* Content description
+
+Additionally, it provides a method to notify SystemUI that the information may have changed and the tile should be refreshed.
+
+#### TileService
+
+This is an abstract Service that needs to be implemented by the developer. The Service manifest must have the permission `android.permission.BIND_QUICK_SETTINGS_TILE` and must respond to the action `android.service.quicksettings.action.QS_TILE`. This will allow SystemUI to find the available tiles and display them to the user.
+
+The implementer is responsible for creating the methods that will respond to the following calls from SystemUI:
+
+* **`onTileAdded`**: called when the tile is added to QS.
+* **`onTileRemoved`**: called when the tile is removed from QS.
+* **`onStartListening`**: called when QS is opened and the tile is showing. This marks the start of the window when calling `getQSTile` is safe and will provide the correct object.
+* **`onStopListening`**: called when QS is closed or the tile is no longer visible by the user. This marks the end of the window described in `onStartListening`.
+* **`onClick`**: called when the user clicks on the tile.
+
+Additionally, the following final methods are provided:
+
+* ```java
+  public final Tile getQsTile()
+  ```
+
+  Provides the tile object that can be modified. This should only be called in the window between `onStartListening` and `onStopListening`.
+
+* ```java
+  public final boolean isLocked()
+
+  public final boolean isSecure()
+  ```
+
+  Provide information about the secure state of the device. This can be used by the tile to accept or reject actions on the tile.
+
+* ```java
+  public final void unlockAndRun(Runnable)
+  ```
+
+  May prompt the user to unlock the device if locked. Once the device is unlocked, it runs the given `Runnable`.
+
+* ```java
+  public final void showDialog(Dialog)
+  ```
+
+  Shows the provided dialog.
+
+##### Binding
+
+When the Service is bound, a callback Binder is provided by SystemUI for all the callbacks, as well as an identifier token (`Binder`). This token is used in the callbacks to identify this `TileService` and match it to the corresponding tile.
+
+The tiles are bound once immediately on creation. After that, the tile is bound whenever it should start listening. When the panels are closed, and the tile is set to stop listening, it will be unbound after a delay of `TileServiceManager#UNBIND_DELAY` (30s), if it's not set to listening again.
+
+##### Active tile
+
+A `TileService` can be declared as an active tile by adding specific meta-data to its manifest (see [TileService#META_DATA_ACTIVE_TILE](https://developer.android.com/reference/android/service/quicksettings/TileService#META_DATA_ACTIVE_TILE)). In this case, it won't receive a call of `onStartListening` when QS is opened. Instead, the tile must request listening status by making a call to `TileService#requestListeningState` with its component name. This will initiate a window that will last until the tile is updated.
+
+The tile will also be granted listening status if it's clicked by the user.
+
+### SystemUI classes
+
+The following sections describe the classes that live in SystemUI to support third party tiles. These classes live in [SystemUI/src/com/android/systemui/qs/external](/packages/SystemUI/src/com/android/systemui/qs/external/)
+
+#### CustomTile
+
+This class is an subclass of `QSTileImpl` to be used with third party tiles. It provides similar behavior to SystemUI tiles as well as handling exclusive behavior like lifting default icons and labels from the application manifest.
+
+#### TileServices
+
+This class is the central controller for all tile services that are currently in Quick Settings as well as provides the support for starting new ones. It is also an implementation of the `Binder` that receives all calls from current `TileService` components and dispatches them to SystemUI or the corresponding `CustomTile`.
+
+Whenever a binder call is made to this class, it matches the corresponding token assigned to the `TileService` with the `ComponentName` and verifies that the call comes from the right UID to prevent spoofing.
+
+As this class is the only one that's aware of every `TileService` that's currently bound, it is also in charge of requesting some to be unbound whenever there is a low memory situation.
+
+#### TileLifecycleManager
+
+This class is in charge of binding and unbinding to a particular `TileService` when necessary, as well as sending the corresponding binder calls. It does not decide whether the tile should be bound or unbound, unless it's requested to process a message. It additionally handles errors in the `Binder` as well as changes in the corresponding component (like updates and enable/disable).
+
+The class has a queue that stores requests while the service is not bound, to be processed as soon as the service is bound.
+
+Each `TileService` gets assigned an exclusive `TileLifecycleManager` when its corresponding tile is added to the set of current ones and kept as long as the tile is available to the user.
+
+#### TileServiceManager
+
+Each instance of this class is an intermediary between the `TileServices` controller and a `TileLifecycleManager` corresponding to a particular `TileService`.
+
+This class handles management of the service, including:
+
+* Deciding when to bind and unbind, requesting it to the `TileLifecycleManager`.
+* Relaying messages to the `TileService` through the `TileLifecycleManager`.
+* Determining the service's bind priority (to deal with OOM situations).
+* Detecting when the package/component has been removed in order to remove the tile and references to it.
+
+## How are tiles created/instantiated?
+
+This section describes the classes that aid in the creation of each tile as well as the complete lifecycle of a tile. First we describe two important interfaces/classes.
+
+### QSTileHost
+
+This class keeps track of the tiles selected by the current user (backed in the Secure Setting `sysui_qs_tiles`) to be displayed in Quick Settings. Whenever the value of this setting changes (or on device start), the whole list of tiles is read. This is compared with the current tiles, destroying unnecessary ones and creating needed ones.
+
+It additionally provides a point of communication between the tiles and the StatusBar, for example to open it and collapse it. And a way for the StatusBar service to add tiles (only works for `CustomTile`).
+
+#### Tile specs
+
+Each single tile is identified by a spec, which is a unique String for that type of tile. The current tiles are stored as a Setting string of comma separated values of these specs. Additionally, the default tiles (that appear on a fresh system) configuration value is stored likewise.
+
+SystemUI tile specs are usually a single simple word identifying the tile (like `wifi` or `battery`). Custom tile specs are always a string of the form `custom(...)` where the ellipsis is a flattened String representing the `ComponentName` for the corresponding `TileService`.
+
+### QSFactory
+
+This interface provides a way of creating tiles and views from a spec. It can be used in plugins to provide different definitions for tiles.
+
+In SystemUI there is only one implementation of this factory and that is the default factory (`QSFactoryImpl`) in `QSTileHost`.
+
+#### QSFactoryImpl
+
+This class implements two methods as specified in the `QSFactory` interface:
+
+* ```java
+  public QSTile createTile(String)
+  ```
+
+  Creates a tile (backend) from a given spec. The factory has providers for all of the SystemUI tiles, returning one when the correct spec is used.
+
+  If the spec is not recognized but it has the `custom(` prefix, the factory tries to create a `CustomTile` for the component in the spec. This could fail (the component is not a valid `TileService` or is not enabled) and will be detected later when the tile is polled to determine if it's available.
+
+* ```java
+  public QSTileView createTileView(QSTile, boolean)
+  ```
+
+  Creates a view for the corresponding `QSTile`. The second parameter determines if the view that is created should be a collapsed one (for using in QQS) or not (for using in QS).
+
+### Lifecycle of a Tile
+
+We describe first the parts of the lifecycle that are common to SystemUI tiles and third party tiles. Following that, there will be a section with the steps that are exclusive to third party tiles.
+
+1. The tile is added through the QS customizer by the user. This will immediately save the new list of tile specs to the Secure Setting `sysui_qs_tiles`. This step could also happend if `StatusBar` adds tiles (either through adb, or through its service interface as with the `DevelopmentTiles`).
+2. This triggers a "setting changed" that is caught by `QSTileHost`. This class processes the new value of the setting and finds out that there is a new spec in the list. Alternatively, when the device is booted, all tiles in the setting are considered as "new".
+3. `QSTileHost` calls all the available `QSFactory` classes that it has registered in order to find the first one that will be able to create a tile with that spec. Assume that `QSFactoryImpl` managed to create the tile, which is some implementation of `QSTile` (either a SystemUI subclass of `QSTileImpl` or a `CustomTile`). If the tile is available, it's stored in a map and things proceed forward.
+4. `QSTileHost` calls its callbacks indicating that the tiles have changed. In particular, `QSPanel` and `QuickQSPanel` receive this call with the full list of tiles. We will focus on these two classes.
+5. For each tile in this list, a `QSTileView` is created (collapsed or expanded) and attached to a `TileRecord` containing the tile backend and the view. Additionally:
+    * a callback is attached to the tile to communicate between the backend and the view or the panel.
+    * the click listeners in the tile are attached to those of the view.
+6. The tile view is added to the corresponding layout.
+
+When the tile is removed from the list of current tiles, all these classes are properly disposed including removing the callbacks and making sure that the backends remove themselves from the controllers they were listening to.
+
+#### Lifecycle of a CustomTile
+
+In step 3 of the previous process, when a `CustomTile` is created, additional steps are taken to ensure the proper binding to the service as described in [Third party tiles (TileService)](#third-party-tiles-tileservice).
+
+1. The `CustomTile` obtains the `TileServices` class from the `QSTileHost` and request the creation of a `TileServiceManager` with its token. As the spec for the `CustomTile` contains the `ComponentName` of the associated service, this can be used to bind to it.
+2. The `TileServiceManager` creates its own `TileLifecycleManager` to take care of binding to the service.
+3. `TileServices` creates maps between the token, the `CustomTile`, the `TileServiceManager`, the token and the `ComponentName`.
+
+## Implementing a tile
+
+This section describes necessary and recommended steps when implementing a Quick Settings tile. Some of them are optional and depend on the requirements of the tile.
+
+### Implementing a SystemUI tile
+
+1. Create a class (preferably in [`SystemUI/src/com/android/systemui/qs/tiles`](/packages/SystemUI/src/com/android/systemui/qs/tiles)) implementing `QSTileImpl` with a particular type of `State` as a parameter.
+2. Create an injectable constructor taking a `QSHost` and whichever classes are needed for the tile's operation. Normally this would be other SystemUI controllers.
+3. Implement the methods described in [Abstract methods in QSTileImpl](#abstract-methods-in-qstileimpl). Look at other tiles for help. Some considerations to have in mind:
+    * If the tile will not support long click (like the `FlashlightTile`), set `state.handlesLongClick` to `false` (maybe in `newTileState`).
+    * Changes to the tile state (either from controllers or from clicks) should call `refreshState`.
+    * Use only `handleUpdateState` to modify the values of the state to the new ones. This can be done by polling controllers or through the `arg` parameter.
+    * If the controller is not a `CallbackController`, respond to `handleSetListening` by attaching/dettaching from controllers.
+    * Implement `isAvailable` so the tile will not be created when it's not necessary.
+4. In `QSFactoryImpl`:
+    * Inject a `Provider` for the tile created before.
+    * Add a case to the `switch` with a unique String spec for the chosen tile.
+5. In [SystemUI/res/values/config.xml](/packages/SystemUI/res/values/config.xml), modify `quick_settings_tiles_stock` and add the spec defined in the previous step. If necessary, add it also to `quick_settings_tiles_default`. The first one contains a list of all the tiles that SystemUI knows how to create (to show to the user in the customization screen). The second one contains only the default tiles that the user will experience on a fresh boot or after they reset their tiles.
+
+#### Abstract methods in QSTileImpl
+
+Following are methods that need to be implemented when creating a new SystemUI tile. `TState` is a type variable of type `State`.
+
+* ```java
+    public TState newTileState()
+  ```
+
+    Creates a new `State` for this tile to use. Each time the state changes, it is copied into a new one and the corresponding fields are modified. The framework provides `State`, `BooleanState` (has an on and off state and provides this as a content description), `SignalState` (`BooleanState` with `activityIn` and `activityOut`), and `SlashState` (can be rotated or slashed through).
+
+    If a tile has special behavior (no long click, no ripple), it can be set in its state here.
+
+* ```java
+    public void handleSetListening(boolean)
+    ```
+
+    Initiates or terminates listening behavior, like listening to Callbacks from controllers. This gets triggered when QS is expanded or collapsed (i.e., when the tile is visible and actionable). Most tiles (like `WifiTile`) do not implement this. Instead, Tiles are LifecycleOwner and are marked as `RESUMED` or `DESTROYED` in `QSTileImpl#handleListening` and handled as part of the lifecycle of [CallbackController](/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackController.java)
+
+* ```java
+    public QSIconView createTileView(Context)
+  ```
+
+  Allows a Tile to use a `QSIconView` different from `QSIconViewImpl` (see [Tile views](#tile-views)), which is the default defined in `QSTileImpl`
+
+* ```java
+    public Intent getLongClickIntent()
+  ```
+
+  Determines the `Intent` launched when the Tile is long pressed.
+
+* ```java
+    protected void handleClick()
+
+    protected void handleSecondaryClick()
+
+    protected void handleLongClick()
+  ```
+
+  Handles what to do when the Tile is clicked. In general, a Tile will make calls to its controller here and maybe update its state immediately (by calling `QSTileImpl#refreshState`). A Tile can also decide to ignore the click here, if it's `Tile#STATE_UNAVAILABLE`.
+
+  By default long click redirects to click and long click launches the intent defined in `getLongClickIntent`.
+
+* ```java
+    protected void handleUpdateState(TState, Object)
+  ```
+
+  Updates the `State` of the Tile based on the state of the device as provided by the respective controller. It will be called every time the Tile becomes visible, is interacted with or `QSTileImpl#refreshState` is called. After this is done, the updated state will be reflected in the UI.
+
+* ```java
+  public int getMetricsCategory()
+  ```
+
+  Identifier for this Tile, as defined in [proto/src/metrics_constants/metrics_constants.proto](/proto/src/metrics_constants/metrics_constants.proto). This is used to log events related to this Tile.
+
+* ```java
+  public boolean isAvailable()
+  ```
+
+  Determines if a Tile is available to be used (for example, disable `WifiTile` in devices with no Wifi support). If this is false, the Tile will be destroyed upon creation.
+
+* ```java
+  public CharSequence getTileLabel()
+  ```
+
+  Provides a default label for this Tile. Used by the QS Panel customizer to show a name next to each available tile.
+
+### Implementing a third party tile
+
+For information about this, use the Android Developer documentation for [TileService](https://developer.android.com/reference/android/service/quicksettings/TileService).
\ No newline at end of file
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 01811e9..f607cc8 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
@@ -118,6 +118,7 @@
         public CharSequence label;
         public CharSequence secondaryLabel;
         public CharSequence contentDescription;
+        public CharSequence stateDescription;
         public CharSequence dualLabelContentDescription;
         public boolean disabledByPolicy;
         public boolean dualTarget = false;
@@ -135,6 +136,7 @@
                     || !Objects.equals(other.label, label)
                     || !Objects.equals(other.secondaryLabel, secondaryLabel)
                     || !Objects.equals(other.contentDescription, contentDescription)
+                    || !Objects.equals(other.stateDescription, stateDescription)
                     || !Objects.equals(other.dualLabelContentDescription,
                             dualLabelContentDescription)
                     || !Objects.equals(other.expandedAccessibilityClassName,
@@ -151,6 +153,7 @@
             other.label = label;
             other.secondaryLabel = secondaryLabel;
             other.contentDescription = contentDescription;
+            other.stateDescription = stateDescription;
             other.dualLabelContentDescription = dualLabelContentDescription;
             other.expandedAccessibilityClassName = expandedAccessibilityClassName;
             other.disabledByPolicy = disabledByPolicy;
@@ -177,6 +180,7 @@
             sb.append(",label=").append(label);
             sb.append(",secondaryLabel=").append(secondaryLabel);
             sb.append(",contentDescription=").append(contentDescription);
+            sb.append(",stateDescription=").append(stateDescription);
             sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
             sb.append(",expandedAccessibilityClassName=").append(expandedAccessibilityClassName);
             sb.append(",disabledByPolicy=").append(disabledByPolicy);
diff --git a/packages/SystemUI/res-keyguard/layout/controls_management.xml b/packages/SystemUI/res-keyguard/layout/controls_management.xml
deleted file mode 100644
index 8330258..0000000
--- a/packages/SystemUI/res-keyguard/layout/controls_management.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:gravity="center_horizontal"
-    android:paddingTop="@dimen/controls_management_top_padding"
-    android:paddingStart="@dimen/controls_management_side_padding"
-    android:paddingEnd="@dimen/controls_management_side_padding" >
-
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textAppearance="?android:attr/textAppearanceLarge"
-        android:textSize="@dimen/controls_title_size"
-        android:textAlignment="center" />
-
-    <TextView
-        android:id="@+id/subtitle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/controls_management_titles_margin"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:textAlignment="center" />
-
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/list"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/controls_management_list_margin" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/controls_zone_header.xml b/packages/SystemUI/res-keyguard/layout/controls_zone_header.xml
new file mode 100644
index 0000000..7b43a03
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/controls_zone_header.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:textAppearance="@style/TextAppearance.Control.Title"
+    android:textColor="?android:attr/colorPrimary"
+    android:layout_marginStart="12dp"
+    android:layout_marginEnd="2dp"
+    android:layout_marginTop="8dp"
+    android:layout_marginBottom="4dp">
+
+</TextView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_cancel_24.xml b/packages/SystemUI/res/drawable/ic_cancel_24.xml
new file mode 100644
index 0000000..8ab28dd
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_cancel_24.xml
@@ -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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_important.xml b/packages/SystemUI/res/drawable/ic_important.xml
new file mode 100644
index 0000000..d7439e1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_important.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M4,18.99h11c0.67,0 1.27,-0.32 1.63,-0.83L21,12l-4.37,-6.16C16.27,5.33 15.67,5 15,5H4l5,7 -5,6.99z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_important_outline.xml b/packages/SystemUI/res/drawable/ic_important_outline.xml
new file mode 100644
index 0000000..7a628bb
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_important_outline.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorControlNormal">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M15,19L3,19l4.5,-7L3,5h12c0.65,0 1.26,0.31 1.63,0.84L21,12l-4.37,6.16c-0.37,0.52 -0.98,0.84 -1.63,0.84zM6.5,17L15,17l3.5,-5L15,7L6.5,7l3.5,5 -3.5,5z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_star.xml b/packages/SystemUI/res/drawable/ic_star.xml
deleted file mode 100644
index 4a731b3..0000000
--- a/packages/SystemUI/res/drawable/ic_star.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-  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
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="@android:color/white"
-        android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27z"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_star_border.xml b/packages/SystemUI/res/drawable/ic_star_border.xml
deleted file mode 100644
index 9ede40b..0000000
--- a/packages/SystemUI/res/drawable/ic_star_border.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-  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
-  -->
-
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:fillColor="@android:color/white"
-        android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
-</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/screenshot_cancel.xml b/packages/SystemUI/res/drawable/screenshot_cancel.xml
new file mode 100644
index 0000000..be3c598
--- /dev/null
+++ b/packages/SystemUI/res/drawable/screenshot_cancel.xml
@@ -0,0 +1,28 @@
+<?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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48dp"
+        android:height="48dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:pathData="M24,24m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"
+        android:fillColor="@android:color/white"/>
+    <path
+        android:fillColor="@color/GM2_grey_500"
+        android:pathData="M31,18.41L29.59,17 24,22.59 18.41,17 17,18.41 22.59,24 17,29.59 18.41,31 24,25.41 29.59,31 31,29.59 25.41,24z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_base_item.xml b/packages/SystemUI/res/layout/controls_base_item.xml
index 68c8246..823bbcd 100644
--- a/packages/SystemUI/res/layout/controls_base_item.xml
+++ b/packages/SystemUI/res/layout/controls_base_item.xml
@@ -72,8 +72,8 @@
     <CheckBox
         android:id="@+id/favorite"
         android:visibility="gone"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"/>
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml
new file mode 100644
index 0000000..a7379be
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_management.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_horizontal"
+    android:paddingTop="@dimen/controls_management_top_padding"
+    android:paddingStart="@dimen/controls_management_side_padding"
+    android:paddingEnd="@dimen/controls_management_side_padding" >
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:textSize="@dimen/controls_title_size"
+        android:textAlignment="center" />
+
+    <TextView
+        android:id="@+id/subtitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/controls_management_titles_margin"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:textAlignment="center" />
+
+    <androidx.core.widget.NestedScrollView
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="vertical"
+        android:layout_marginTop="@dimen/controls_management_list_margin">
+
+        <ViewStub
+            android:id="@+id/stub"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+
+    </androidx.core.widget.NestedScrollView>
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="64dp">
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/controls_app_divider_height"
+            android:layout_gravity="center_horizontal|top"
+            android:background="?android:attr/listDivider" />
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:padding="4dp">
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:text="See other apps"
+                android:textAppearance="@style/TextAppearance.Control.Title"
+                android:textColor="?android:attr/colorPrimary"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <Button
+                android:id="@+id/done"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:text="Done"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"/>
+        </androidx.constraintlayout.widget.ConstraintLayout>
+    </FrameLayout>
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_management_apps.xml b/packages/SystemUI/res/layout/controls_management_apps.xml
new file mode 100644
index 0000000..2bab433
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_management_apps.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+
+<androidx.recyclerview.widget.RecyclerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/list"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+
+</androidx.recyclerview.widget.RecyclerView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_management_favorites.xml b/packages/SystemUI/res/layout/controls_management_favorites.xml
new file mode 100644
index 0000000..a36dd12
--- /dev/null
+++ b/packages/SystemUI/res/layout/controls_management_favorites.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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.
+  -->
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/text_favorites"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="FAVORITES"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/divider1"
+        app:layout_constraintTop_toTopOf="parent"
+        />
+
+    <View
+        android:id="@+id/divider1"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/controls_app_divider_height"
+        android:layout_gravity="center_horizontal|top"
+        android:background="?android:attr/listDivider"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/listFavorites"
+        app:layout_constraintTop_toBottomOf="@id/text_favorites"
+        />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/listFavorites"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginTop="@dimen/controls_management_list_margin"
+        android:nestedScrollingEnabled="false"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/text_all"
+        app:layout_constraintTop_toBottomOf="@id/divider1"/>
+
+    <TextView
+        android:id="@+id/text_all"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/controls_management_list_margin"
+        android:text="ALL"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/divider2"
+        app:layout_constraintTop_toBottomOf="@id/listFavorites"
+        />
+
+    <View
+        android:id="@+id/divider2"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/controls_app_divider_height"
+        android:layout_gravity="center_horizontal|top"
+        android:background="?android:attr/listDivider"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/listAll"
+        app:layout_constraintTop_toBottomOf="@id/text_all"
+        />
+
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/listAll"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_marginTop="@dimen/controls_management_list_margin"
+        android:nestedScrollingEnabled="false"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/divider2"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml
index 40b2476..2cd9505 100644
--- a/packages/SystemUI/res/layout/controls_with_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_with_favorites.xml
@@ -1,3 +1,18 @@
+<!--
+  ~ 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.
+  -->
 <merge
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index 1f7def2..d0151ff 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -55,10 +55,22 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        android:elevation="8dp"
+        android:elevation="@dimen/screenshot_preview_elevation"
         android:visibility="gone"
         android:background="@drawable/screenshot_rounded_corners"
         android:adjustViewBounds="true"/>
+    <FrameLayout
+        android:id="@+id/global_screenshot_dismiss_button"
+        android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"
+        android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
+        android:elevation="9dp"
+        android:visibility="gone">
+        <ImageView
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_margin="@dimen/screenshot_dismiss_button_margin"
+            android:src="@drawable/screenshot_cancel"/>
+    </FrameLayout>
     <ImageView
         android:id="@+id/global_screenshot_flash"
         android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index a9d6e35..8460612 100644
--- a/packages/SystemUI/res/layout/notification_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_conversation_info.xml
@@ -28,7 +28,7 @@
     android:paddingStart="@*android:dimen/notification_content_margin_start">
 
     <!-- Package Info -->
-    <RelativeLayout
+    <LinearLayout
         android:id="@+id/header"
         android:layout_width="match_parent"
         android:layout_height="@dimen/notification_guts_conversation_header_height"
@@ -41,16 +41,20 @@
             android:layout_height="@dimen/notification_guts_conversation_icon_size"
             android:layout_centerVertical="true"
             android:layout_alignParentStart="true"
-            android:layout_marginEnd="6dp" />
+            android:layout_marginEnd="15dp" />
         <LinearLayout
             android:id="@+id/names"
+            android:layout_weight="1"
+            android:layout_width="0dp"
             android:orientation="vertical"
-            android:layout_width="wrap_content"
+
             android:layout_height="wrap_content"
             android:minHeight="@dimen/notification_guts_conversation_icon_size"
             android:layout_centerVertical="true"
             android:gravity="center_vertical"
-            android:layout_toEndOf="@id/conversation_icon">
+            android:layout_alignEnd="@id/conversation_icon"
+            android:layout_toEndOf="@id/conversation_icon"
+            android:layout_alignStart="@id/mute">
             <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
@@ -107,67 +111,40 @@
                     android:layout_weight="1"
                     style="@style/TextAppearance.NotificationImportanceChannel"/>
             </LinearLayout>
+            <TextView
+                android:id="@+id/delegate_name"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerVertical="true"
+                style="@style/TextAppearance.NotificationImportanceHeader"
+                android:layout_marginStart="2dp"
+                android:layout_marginEnd="2dp"
+                android:ellipsize="end"
+                android:text="@string/notification_delegate_header"
+                android:layout_toEndOf="@id/pkg_divider"
+                android:maxLines="1" />
 
         </LinearLayout>
 
-        <TextView
-            android:id="@+id/pkg_divider"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_centerVertical="true"
-            style="@style/TextAppearance.NotificationImportanceHeader"
-            android:layout_marginStart="2dp"
-            android:layout_marginEnd="2dp"
-            android:layout_toEndOf="@id/name"
-            android:text="@*android:string/notification_header_divider_symbol" />
-        <TextView
-            android:id="@+id/delegate_name"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_centerVertical="true"
-            style="@style/TextAppearance.NotificationImportanceHeader"
-            android:layout_marginStart="2dp"
-            android:layout_marginEnd="2dp"
-            android:ellipsize="end"
-            android:text="@string/notification_delegate_header"
-            android:layout_toEndOf="@id/pkg_divider"
-            android:maxLines="1" />
-
         <!-- end aligned fields -->
         <ImageButton
-            android:id="@+id/demote"
+            android:id="@+id/mute"
             android:layout_width="@dimen/notification_importance_toggle_size"
             android:layout_height="@dimen/notification_importance_toggle_size"
             android:layout_centerVertical="true"
             android:background="@drawable/ripple_drawable"
-            android:contentDescription="@string/demote"
-            android:src="@drawable/ic_demote_conversation"
-            android:layout_toStartOf="@id/app_settings"
-            android:tint="@color/notification_guts_link_icon_tint"/>
-        <!-- Optional link to app. Only appears if the channel is not disabled and the app
-asked for it -->
-        <ImageButton
-            android:id="@+id/app_settings"
-            android:layout_width="@dimen/notification_importance_toggle_size"
-            android:layout_height="@dimen/notification_importance_toggle_size"
-            android:layout_centerVertical="true"
-            android:visibility="gone"
-            android:background="@drawable/ripple_drawable"
-            android:contentDescription="@string/notification_app_settings"
-            android:src="@drawable/ic_info"
-            android:layout_toStartOf="@id/info"
+            android:layout_toStartOf="@id/fave"
             android:tint="@color/notification_guts_link_icon_tint"/>
         <ImageButton
-            android:id="@+id/info"
+            android:id="@+id/fave"
             android:layout_width="@dimen/notification_importance_toggle_size"
             android:layout_height="@dimen/notification_importance_toggle_size"
             android:layout_centerVertical="true"
             android:background="@drawable/ripple_drawable"
-            android:contentDescription="@string/notification_more_settings"
-            android:src="@drawable/ic_settings"
             android:layout_alignParentEnd="true"
             android:tint="@color/notification_guts_link_icon_tint"/>
-    </RelativeLayout>
+
+    </LinearLayout>
 
     <LinearLayout
         android:id="@+id/actions"
@@ -182,6 +159,23 @@
             android:layout_width="match_parent"
             android:layout_height="0.5dp"
             android:background="@color/GM2_grey_300" />
+
+        <Button
+            android:id="@+id/snooze"
+            android:layout_height="@dimen/notification_guts_conversation_action_height"
+            android:layout_width="match_parent"
+            style="?android:attr/borderlessButtonStyle"
+            android:text="@string/notification_menu_snooze_action"
+            android:gravity="left|center_vertical"
+            android:drawableStart="@drawable/ic_snooze"
+            android:drawablePadding="@dimen/notification_guts_conversation_action_text_padding_start"
+            android:drawableTint="@color/notification_guts_link_icon_tint"/>
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="0.5dp"
+            android:background="@color/GM2_grey_300" />
+
         <Button
             android:id="@+id/bubble"
             android:layout_height="@dimen/notification_guts_conversation_action_height"
@@ -212,42 +206,15 @@
             android:layout_width="match_parent"
             android:layout_height="0.5dp"
             android:background="@color/GM2_grey_300" />
-        <Button
-            android:id="@+id/fave"
-            android:layout_height="@dimen/notification_guts_conversation_action_height"
-            android:layout_width="match_parent"
-            style="?android:attr/borderlessButtonStyle"
-            android:gravity="left|center_vertical"
-            android:drawablePadding="@dimen/notification_guts_conversation_action_text_padding_start"
-            android:drawableTint="@color/notification_guts_link_icon_tint"/>
 
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="0.5dp"
-            android:background="@color/GM2_grey_300" />
         <Button
-            android:id="@+id/snooze"
+            android:id="@+id/info"
             android:layout_height="@dimen/notification_guts_conversation_action_height"
             android:layout_width="match_parent"
             style="?android:attr/borderlessButtonStyle"
-            android:text="@string/notification_menu_snooze_action"
+            android:drawableStart="@drawable/ic_settings"
+            android:text="@string/notification_menu_settings_action"
             android:gravity="left|center_vertical"
-            android:drawableStart="@drawable/ic_snooze"
-            android:drawablePadding="@dimen/notification_guts_conversation_action_text_padding_start"
-            android:drawableTint="@color/notification_guts_link_icon_tint"/>
-
-        <View
-            android:layout_width="match_parent"
-            android:layout_height="0.5dp"
-            android:background="@color/GM2_grey_300" />
-        <Button
-            android:id="@+id/mute"
-            android:layout_height="@dimen/notification_guts_conversation_action_height"
-            android:layout_width="match_parent"
-            style="?android:attr/borderlessButtonStyle"
-            android:text="@string/notification_conversation_mute"
-            android:gravity="left|center_vertical"
-            android:drawableStart="@drawable/ic_notifications_silence"
             android:drawablePadding="@dimen/notification_guts_conversation_action_text_padding_start"
             android:drawableTint="@color/notification_guts_link_icon_tint"/>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7a3395c..0db7d67 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -103,7 +103,8 @@
     <item name="status_bar_icon_scale_factor" format="float" type="dimen">1.0</item>
 
     <dimen name="group_overflow_number_size">@*android:dimen/notification_text_size</dimen>
-    <dimen name="group_overflow_number_padding">@*android:dimen/notification_content_margin_end</dimen>
+    <dimen name="group_overflow_number_padding">@*android:dimen/notification_content_margin_end
+    </dimen>
 
     <!-- max height of a notification such that the content can still fade out when closing -->
     <dimen name="max_notification_fadeout_height">100dp</dimen>
@@ -267,7 +268,9 @@
     <dimen name="status_bar_icon_drawing_size">15dp</dimen>
 
     <!-- size at which Notification icons will be drawn on Ambient Display -->
-    <dimen name="status_bar_icon_drawing_size_dark">@*android:dimen/notification_header_icon_size_ambient</dimen>
+    <dimen name="status_bar_icon_drawing_size_dark">
+        @*android:dimen/notification_header_icon_size_ambient
+    </dimen>
 
     <!-- size of notification icons when the notifications are hidden -->
     <dimen name="hidden_shelf_icon_size">16dp</dimen>
@@ -299,8 +302,11 @@
     <dimen name="global_screenshot_legacy_bg_padding">20dp</dimen>
     <dimen name="global_screenshot_bg_padding">20dp</dimen>
     <dimen name="global_screenshot_x_scale">80dp</dimen>
+    <dimen name="screenshot_preview_elevation">8dp</dimen>
     <dimen name="screenshot_offset_y">48dp</dimen>
     <dimen name="screenshot_offset_x">16dp</dimen>
+    <dimen name="screenshot_dismiss_button_tappable_size">48dp</dimen>
+    <dimen name="screenshot_dismiss_button_margin">8dp</dimen>
     <dimen name="screenshot_action_container_offset_y">32dp</dimen>
     <dimen name="screenshot_action_container_corner_radius">10dp</dimen>
     <dimen name="screenshot_action_container_padding_vertical">10dp</dimen>
@@ -597,10 +603,14 @@
 
     <!-- The height of the divider between the individual notifications in a notification
          group. -->
-    <dimen name="notification_children_container_divider_height">@dimen/notification_divider_height</dimen>
+    <dimen name="notification_children_container_divider_height">
+        @dimen/notification_divider_height
+    </dimen>
 
     <!-- The top margin for the notification children container in its non-expanded form. -->
-    <dimen name="notification_children_container_margin_top">@*android:dimen/notification_content_margin_top</dimen>
+    <dimen name="notification_children_container_margin_top">
+        @*android:dimen/notification_content_margin_top
+    </dimen>
 
     <!-- The height of a notification header -->
     <dimen name="notification_header_height">53dp</dimen>
@@ -1061,9 +1071,12 @@
     <integer name="wireless_charging_scale_dots_duration">83</integer>
     <integer name="wireless_charging_num_dots">16</integer>
     <!-- Starting text size in sp of batteryLevel for wireless charging animation -->
-    <item name="wireless_charging_anim_battery_level_text_size_start" format="float" type="dimen">0</item>
+    <item name="wireless_charging_anim_battery_level_text_size_start" format="float" type="dimen">
+        0
+    </item>
     <!-- Ending text size in sp of batteryLevel for wireless charging animation -->
-    <item name="wireless_charging_anim_battery_level_text_size_end" format="float" type="dimen">24</item>
+    <item name="wireless_charging_anim_battery_level_text_size_end" format="float" type="dimen">24
+    </item>
     <!-- time until battery info is at full opacity-->
     <integer name="wireless_charging_anim_opacity_offset">80</integer>
     <!-- duration batteryLevel opacity goes from 0 to 1 duration -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b85b51e..8f9e934 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1829,23 +1829,23 @@
     <!-- Notification: Conversation: control panel, label for button that demotes notification from conversation to normal notification -->
     <string name="demote">Mark this notification as not a conversation</string>
 
-    <!-- [CHAR LIMIT=100] Mark this conversation as a favorite -->
-    <string name="notification_conversation_favorite">Mark as important</string>
+    <!-- [CHAR LIMIT=100] This conversation is marked as important -->
+    <string name="notification_conversation_favorite">Important conversation</string>
 
-    <!-- [CHAR LIMIT=100] Unmark this conversation as a favorite -->
-    <string name="notification_conversation_unfavorite">Mark as unimportant</string>
+    <!-- [CHAR LIMIT=100] This conversation is not marked as important -->
+    <string name="notification_conversation_unfavorite">Not an important conversation</string>
 
-    <!-- [CHAR LIMIT=100] Mute this conversation -->
-    <string name="notification_conversation_mute">Silence</string>
+    <!-- [CHAR LIMIT=100] This conversation is silenced (will not make sound or vibrate)-->
+    <string name="notification_conversation_mute">Silenced</string>
 
-    <!-- [CHAR LIMIT=100] Umute this conversation -->
+    <!-- [CHAR LIMIT=100] This conversation is alerting (may make sound and/or vibrate)-->
     <string name="notification_conversation_unmute">Alerting</string>
 
     <!-- [CHAR LIMIT=100] Show notification as bubble -->
-    <string name="notification_conversation_bubble">Show as bubble</string>
+    <string name="notification_conversation_bubble">Show bubble</string>
 
     <!-- [CHAR LIMIT=100] Turn off bubbles for notification -->
-    <string name="notification_conversation_unbubble">Turn off bubbles</string>
+    <string name="notification_conversation_unbubble">Remove bubbles</string>
 
     <!-- [CHAR LIMIT=100] Add this conversation to home screen -->
     <string name="notification_conversation_home_screen">Add to home screen</string>
@@ -1860,7 +1860,10 @@
     <string name="notification_menu_snooze_description">notification snooze options</string>
 
     <!-- Notification: Menu row: Label for the snooze action shown in local context menu. [CHAR LIMIT=NONE] -->
-    <string name="notification_menu_snooze_action">Snooze</string>
+    <string name="notification_menu_snooze_action">Remind me</string>
+
+    <!-- Notification: Menu row: Label for the snooze action shown in local context menu. [CHAR LIMIT=NONE] -->
+    <string name="notification_menu_settings_action">Settings</string>
 
     <!-- Notification: Snooze panel: Snooze undo button label. [CHAR LIMIT=50]-->
     <string name="snooze_undo">UNDO</string>
@@ -2029,6 +2032,9 @@
     <!-- Label for feature switch [CHAR LIMIT=30] -->
     <string name="switch_bar_off">Off</string>
 
+    <!-- The tile in quick settings is unavailable. [CHAR LIMIT=32] -->
+    <string name="tile_unavailable">Unavailable</string>
+
     <!-- SysUI Tuner: Button that leads to the navigation bar customization screen [CHAR LIMIT=60] -->
     <string name="nav_bar">Navigation bar</string>
 
@@ -2583,6 +2589,4 @@
     <string name="controls_favorite_default_title">Controls</string>
     <!-- Controls management controls screen subtitle [CHAR LIMIT=NONE] -->
     <string name="controls_favorite_subtitle">Choose controls for quick access</string>
-
-
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index a378610..b8997c2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -135,8 +135,7 @@
         ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
         for (PluginInfo info : plugins) {
             if (className.startsWith(info.mPackage)) {
-                disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
-                disableAny = true;
+                disableAny |= disable(info, PluginEnabler.DISABLED_FROM_EXPLICIT_CRASH);
             }
         }
         return disableAny;
@@ -144,10 +143,11 @@
 
     public boolean disableAll() {
         ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins);
+        boolean disabledAny = false;
         for (int i = 0; i < plugins.size(); i++) {
-            disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
+            disabledAny |= disable(plugins.get(i), PluginEnabler.DISABLED_FROM_SYSTEM_CRASH);
         }
-        return plugins.size() != 0;
+        return disabledAny;
     }
 
     private boolean isPluginWhitelisted(ComponentName pluginName) {
@@ -166,7 +166,7 @@
         return false;
     }
 
-    private void disable(PluginInfo info, @PluginEnabler.DisableReason int reason) {
+    private boolean disable(PluginInfo info, @PluginEnabler.DisableReason int reason) {
         // Live by the sword, die by the sword.
         // Misbehaving plugins get disabled and won't come back until uninstall/reinstall.
 
@@ -176,10 +176,12 @@
         // assuming one of them must be bad.
         if (isPluginWhitelisted(pluginComponent)) {
             // Don't disable whitelisted plugins as they are a part of the OS.
-            return;
+            return false;
         }
         Log.w(TAG, "Disabling plugin " + pluginComponent.flattenToShortString());
         mManager.getPluginEnabler().setDisabled(pluginComponent, reason);
+
+        return true;
     }
 
     public <T> boolean dependsOn(Plugin p, Class<T> cls) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index bea55c8..2d55a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -146,7 +146,6 @@
             int viewType) {
         BadgedImageView view = (BadgedImageView) LayoutInflater.from(parent.getContext())
                 .inflate(R.layout.bubble_view, parent, false);
-        view.setPadding(15, 15, 15, 15);
         return new ViewHolder(view);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt b/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
index 53841e2..49a16d8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
@@ -20,6 +20,6 @@
 
 data class ControlStatus(
     val control: Control,
-    val favorite: Boolean,
+    var favorite: Boolean,
     val removed: Boolean = false
 )
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index b3ba2b2..fce5041 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -26,10 +26,18 @@
     val available: Boolean
 
     fun getFavoriteControls(): List<ControlInfo>
-    fun loadForComponent(componentName: ComponentName, callback: (List<ControlStatus>) -> Unit)
+    fun loadForComponent(
+        componentName: ComponentName,
+        callback: (List<ControlStatus>, List<String>) -> Unit
+    )
+
     fun subscribeToFavorites()
     fun changeFavoriteStatus(controlInfo: ControlInfo, state: Boolean)
-    fun countFavoritesForComponent(componentName: ComponentName): Int = 0
+    fun replaceFavoritesForComponent(componentName: ComponentName, favorites: List<ControlInfo>)
+
+    fun getFavoritesForComponent(componentName: ComponentName): List<ControlInfo>
+    fun countFavoritesForComponent(componentName: ComponentName): Int
+
     fun unsubscribe()
     fun action(controlInfo: ControlInfo, action: ControlAction)
     fun refreshStatus(componentName: ComponentName, control: Control)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 7de1557..e611197 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -70,9 +70,10 @@
     }
 
     // Map of map: ComponentName -> (String -> ControlInfo).
-    // Only for current user
+    //
     @GuardedBy("currentFavorites")
-    private val currentFavorites = ArrayMap<ComponentName, MutableMap<String, ControlInfo>>()
+    private val currentFavorites = ArrayMap<ComponentName, MutableList<ControlInfo>>()
+            .withDefault { mutableListOf() }
 
     private var userChanging: Boolean = true
 
@@ -180,15 +181,14 @@
         val infos = persistenceWrapper.readFavorites()
         synchronized(currentFavorites) {
             infos.forEach {
-                currentFavorites.getOrPut(it.component, { ArrayMap<String, ControlInfo>() })
-                        .put(it.controlId, it)
+                currentFavorites.getOrPut(it.component, { mutableListOf() }).add(it)
             }
         }
     }
 
     override fun loadForComponent(
         componentName: ComponentName,
-        callback: (List<ControlStatus>) -> Unit
+        callback: (List<ControlStatus>, List<String>) -> Unit
     ) {
         if (!confirmAvailability()) {
             if (userChanging) {
@@ -200,29 +200,34 @@
                         TimeUnit.MILLISECONDS
                 )
             } else {
-                callback(emptyList())
+                callback(emptyList(), emptyList())
             }
             return
         }
         bindingController.bindAndLoad(componentName) {
             synchronized(currentFavorites) {
-                val favoritesForComponentKeys: Set<String> =
-                        currentFavorites.get(componentName)?.keys ?: emptySet()
-                val changed = updateFavoritesLocked(componentName, it)
+                val favoritesForComponentKeys: List<String> =
+                        currentFavorites.getValue(componentName).map { it.controlId }
+                val changed = updateFavoritesLocked(componentName, it, favoritesForComponentKeys)
                 if (changed) {
                     persistenceWrapper.storeFavorites(favoritesAsListLocked())
                 }
-                val removed = findRemovedLocked(favoritesForComponentKeys, it)
-                callback(removed.map { currentFavorites.getValue(componentName).getValue(it) }
-                            .map(::createRemovedStatus) +
-                        it.map { ControlStatus(it, it.controlId in favoritesForComponentKeys) })
+                val removed = findRemovedLocked(favoritesForComponentKeys.toSet(), it)
+                val controlsWithFavorite =
+                        it.map { ControlStatus(it, it.controlId in favoritesForComponentKeys) }
+                callback(
+                        currentFavorites.getValue(componentName)
+                                .filter { it.controlId in removed }
+                                .map(::createRemovedStatus) + controlsWithFavorite,
+                        favoritesForComponentKeys
+                )
             }
         }
     }
 
     private fun createRemovedStatus(controlInfo: ControlInfo): ControlStatus {
         val intent = Intent(context, ControlsFavoritingActivity::class.java).apply {
-            putExtra(ControlsFavoritingActivity.EXTRA_COMPONENT, controlInfo.component)
+            putExtra(Intent.EXTRA_COMPONENT_NAME, controlInfo.component)
             flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP
         }
         val pendingIntent = PendingIntent.getActivity(context,
@@ -243,17 +248,24 @@
     }
 
     @GuardedBy("currentFavorites")
-    private fun updateFavoritesLocked(componentName: ComponentName, list: List<Control>): Boolean {
-        val favorites = currentFavorites.get(componentName) ?: mutableMapOf()
-        val favoriteKeys = favorites.keys
+    private fun updateFavoritesLocked(
+        componentName: ComponentName,
+        list: List<Control>,
+        favoriteKeys: List<String>
+    ): Boolean {
+        val favorites = currentFavorites.get(componentName) ?: mutableListOf()
         if (favoriteKeys.isEmpty()) return false // early return
         var changed = false
-        list.forEach {
-            if (it.controlId in favoriteKeys) {
-                val value = favorites.getValue(it.controlId)
-                if (value.controlTitle != it.title || value.deviceType != it.deviceType) {
-                    favorites[it.controlId] = value.copy(controlTitle = it.title,
-                            deviceType = it.deviceType)
+        list.forEach { control ->
+            if (control.controlId in favoriteKeys) {
+                val index = favorites.indexOfFirst { it.controlId == control.controlId }
+                val value = favorites[index]
+                if (value.controlTitle != control.title ||
+                        value.deviceType != control.deviceType) {
+                    favorites[index] = value.copy(
+                            controlTitle = control.title,
+                            deviceType = control.deviceType
+                    )
                     changed = true
                 }
             }
@@ -263,14 +275,14 @@
 
     @GuardedBy("currentFavorites")
     private fun favoritesAsListLocked(): List<ControlInfo> {
-        return currentFavorites.flatMap { it.value.values }
+        return currentFavorites.flatMap { it.value }
     }
 
     override fun subscribeToFavorites() {
         if (!confirmAvailability()) return
         // Make a copy of the favorites list
         val favorites = synchronized(currentFavorites) {
-            currentFavorites.flatMap { it.value.values.toList() }
+            currentFavorites.flatMap { it.value }
         }
         bindingController.subscribe(favorites)
     }
@@ -286,22 +298,19 @@
         val listOfControls = synchronized(currentFavorites) {
             if (state) {
                 if (controlInfo.component !in currentFavorites) {
-                    currentFavorites.put(controlInfo.component, ArrayMap<String, ControlInfo>())
+                    currentFavorites.put(controlInfo.component, mutableListOf())
                     changed = true
                 }
                 val controlsForComponent = currentFavorites.getValue(controlInfo.component)
-                if (controlInfo.controlId !in controlsForComponent) {
-                    controlsForComponent.put(controlInfo.controlId, controlInfo)
+                if (controlsForComponent.firstOrNull {
+                            it.controlId == controlInfo.controlId
+                        } == null) {
+                    controlsForComponent.add(controlInfo)
                     changed = true
-                } else {
-                    if (controlsForComponent.getValue(controlInfo.controlId) != controlInfo) {
-                        controlsForComponent.put(controlInfo.controlId, controlInfo)
-                        changed = true
-                    }
                 }
             } else {
                 changed = currentFavorites.get(controlInfo.component)
-                        ?.remove(controlInfo.controlId) != null
+                        ?.remove(controlInfo) != null
             }
             favoritesAsListLocked()
         }
@@ -310,6 +319,19 @@
         }
     }
 
+    override fun replaceFavoritesForComponent(
+        componentName: ComponentName,
+        favorites: List<ControlInfo>
+    ) {
+        if (!confirmAvailability()) return
+        val filtered = favorites.filter { it.component == componentName }
+        val listOfControls = synchronized(currentFavorites) {
+            currentFavorites.put(componentName, filtered.toMutableList())
+            favoritesAsListLocked()
+        }
+        persistenceWrapper.storeFavorites(listOfControls)
+    }
+
     override fun refreshStatus(componentName: ComponentName, control: Control) {
         if (!confirmAvailability()) {
             Log.d(TAG, "Controls not available")
@@ -317,7 +339,13 @@
         }
         executor.execute {
             synchronized(currentFavorites) {
-                val changed = updateFavoritesLocked(componentName, listOf(control))
+                val favoriteKeysForComponent =
+                        currentFavorites.get(componentName)?.map { it.controlId } ?: emptyList()
+                val changed = updateFavoritesLocked(
+                        componentName,
+                        listOf(control),
+                        favoriteKeysForComponent
+                )
                 if (changed) {
                     persistenceWrapper.storeFavorites(favoritesAsListLocked())
                 }
@@ -361,6 +389,12 @@
         }
     }
 
+    override fun getFavoritesForComponent(componentName: ComponentName): List<ControlInfo> {
+        return synchronized(currentFavorites) {
+            currentFavorites.get(componentName) ?: emptyList()
+        }
+    }
+
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
         pw.println("ControlsController state:")
         pw.println("  Available: $available")
@@ -368,10 +402,8 @@
         pw.println("  Current user: ${currentUser.identifier}")
         pw.println("  Favorites:")
         synchronized(currentFavorites) {
-            currentFavorites.forEach {
-                it.value.forEach {
-                    pw.println("    ${it.value}")
-                }
+            favoritesAsListLocked().forEach {
+                pw.println("    ${ it }")
             }
         }
         pw.println(bindingController.toString())
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
index b122439..89caace 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
@@ -29,6 +29,7 @@
 import com.android.settingslib.applications.DefaultAppInfo
 import com.android.settingslib.widget.CandidateInfo
 import com.android.systemui.R
+import java.text.Collator
 import java.util.concurrent.Executor
 
 /**
@@ -44,23 +45,27 @@
  * @param onAppSelected a callback to indicate that an app has been selected in the list.
  */
 class AppAdapter(
+    backgroundExecutor: Executor,
     uiExecutor: Executor,
     lifecycle: Lifecycle,
     controlsListingController: ControlsListingController,
     private val layoutInflater: LayoutInflater,
     private val onAppSelected: (ComponentName?) -> Unit = {},
-    private val favoritesRenderer: FavoritesRenderer
+    private val favoritesRenderer: FavoritesRenderer,
+    private val resources: Resources
 ) : RecyclerView.Adapter<AppAdapter.Holder>() {
 
     private var listOfServices = emptyList<CandidateInfo>()
 
     private val callback = object : ControlsListingController.ControlsListingCallback {
         override fun onServicesUpdated(list: List<CandidateInfo>) {
-            uiExecutor.execute {
-                listOfServices = list.sortedBy {
-                    it.loadLabel().toString()
+            backgroundExecutor.execute {
+                val collator = Collator.getInstance(resources.getConfiguration().locale)
+                val localeComparator = compareBy<CandidateInfo, CharSequence>(collator) {
+                    it.loadLabel()
                 }
-                notifyDataSetChanged()
+                listOfServices = list.sortedWith(localeComparator)
+                uiExecutor.execute(::notifyDataSetChanged)
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
index 65dcc2b1..d3cabe6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
@@ -25,101 +25,150 @@
 import android.widget.CheckBox
 import android.widget.ImageView
 import android.widget.TextView
+import androidx.recyclerview.widget.GridLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import com.android.systemui.R
-import com.android.systemui.controls.ControlStatus
-import com.android.systemui.controls.controller.ControlInfo
 import com.android.systemui.controls.ui.RenderInfo
 
+private typealias ModelFavoriteChanger = (String, Boolean) -> Unit
+
 /**
  * Adapter for binding [Control] information to views.
  *
+ * The model for this adapter is provided by a [FavoriteModel] that is set using
+ * [changeFavoritesModel]. This allows for updating the model if there's a reload.
+ *
  * @param layoutInflater an inflater for the views in the containing [RecyclerView]
- * @param favoriteCallback a callback to be called when the favorite status of a [Control] is
- *                         changed. The callback will take a [ControlInfo.Builder] that's
- *                         pre-populated with the [Control] information and the new favorite
- *                         status.
+ * @param onlyFavorites set to true to only display favorites instead of all controls
  */
 class ControlAdapter(
     private val layoutInflater: LayoutInflater,
-    private val favoriteCallback: (ControlInfo.Builder, Boolean) -> Unit
-) : RecyclerView.Adapter<ControlAdapter.Holder>() {
+    private val onlyFavorites: Boolean = false
+) : RecyclerView.Adapter<Holder>() {
 
-    var listOfControls = emptyList<ControlStatus>()
-
-    override fun onCreateViewHolder(parent: ViewGroup, i: Int): Holder {
-        return Holder(layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply {
-            layoutParams.apply {
-                width = ViewGroup.LayoutParams.MATCH_PARENT
-            }
-            elevation = 15f
-        })
+    companion object {
+        private const val TYPE_ZONE = 0
+        private const val TYPE_CONTROL = 1
     }
 
-    override fun getItemCount() = listOfControls.size
+    val spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
+        override fun getSpanSize(position: Int): Int {
+            return if (getItemViewType(position) == TYPE_ZONE) 2 else 1
+        }
+    }
+
+    var modelList: List<ElementWrapper> = emptyList()
+    private var favoritesModel: FavoriteModel? = null
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
+        return when (viewType) {
+            TYPE_CONTROL -> {
+                ControlHolder(
+                        layoutInflater.inflate(R.layout.controls_base_item, parent, false).apply {
+                            layoutParams.apply {
+                                width = ViewGroup.LayoutParams.MATCH_PARENT
+                            }
+                            elevation = 15f
+                        },
+                        { id, favorite ->
+                            favoritesModel?.changeFavoriteStatus(id, favorite)
+                        })
+            }
+            TYPE_ZONE -> {
+                ZoneHolder(layoutInflater.inflate(R.layout.controls_zone_header, parent, false))
+            }
+            else -> throw IllegalStateException("Wrong viewType: $viewType")
+        }
+    }
+
+    fun changeFavoritesModel(favoritesModel: FavoriteModel) {
+        this.favoritesModel = favoritesModel
+        if (onlyFavorites) {
+            modelList = favoritesModel.favorites
+        } else {
+            modelList = favoritesModel.all
+        }
+        notifyDataSetChanged()
+    }
+
+    override fun getItemCount() = modelList.size
 
     override fun onBindViewHolder(holder: Holder, index: Int) {
-        holder.bindData(listOfControls[index], favoriteCallback)
+        holder.bindData(modelList[index])
     }
 
+    override fun getItemViewType(position: Int): Int {
+        return when (modelList[position]) {
+            is ZoneNameWrapper -> TYPE_ZONE
+            is ControlWrapper -> TYPE_CONTROL
+        }
+    }
+}
+
+/**
+ * Holder for binding views in the [RecyclerView]-
+ * @param view the [View] for this [Holder]
+ */
+sealed class Holder(view: View) : RecyclerView.ViewHolder(view) {
+
     /**
-     * Holder for binding views in the [RecyclerView]-
+     * Bind the data from the model into the view
      */
-    class Holder(view: View) : RecyclerView.ViewHolder(view) {
-        private val icon: ImageView = itemView.requireViewById(R.id.icon)
-        private val title: TextView = itemView.requireViewById(R.id.title)
-        private val subtitle: TextView = itemView.requireViewById(R.id.subtitle)
-        private val removed: TextView = itemView.requireViewById(R.id.status)
-        private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite).apply {
-            visibility = View.VISIBLE
-        }
+    abstract fun bindData(wrapper: ElementWrapper)
+}
 
-        /**
-         * Bind data to the view
-         * @param data information about the [Control]
-         * @param callback a callback to be called when the favorite status of the [Control] is
-         *                 changed. The callback will take a [ControlInfo.Builder] that's
-         *                 pre-populated with the [Control] information and the new favorite status.
-         */
-        fun bindData(data: ControlStatus, callback: (ControlInfo.Builder, Boolean) -> Unit) {
-            val renderInfo = getRenderInfo(data.control.deviceType, data.favorite)
-            title.text = data.control.title
-            subtitle.text = data.control.subtitle
-            favorite.isChecked = data.favorite
-            removed.text = if (data.removed) "Removed" else ""
-            favorite.setOnClickListener {
-                val infoBuilder = ControlInfo.Builder().apply {
-                    controlId = data.control.controlId
-                    controlTitle = data.control.title
-                    deviceType = data.control.deviceType
-                }
-                callback(infoBuilder, favorite.isChecked)
-            }
-            itemView.setOnClickListener {
-                favorite.performClick()
-            }
-            applyRenderInfo(renderInfo)
-        }
+/**
+ * Holder for using with [ZoneNameWrapper] to display names of zones.
+ */
+private class ZoneHolder(view: View) : Holder(view) {
+    private val zone: TextView = itemView as TextView
 
-        private fun getRenderInfo(
-            @DeviceTypes.DeviceType deviceType: Int,
-            favorite: Boolean
-        ): RenderInfo {
-            return RenderInfo.lookup(deviceType, favorite)
-        }
+    override fun bindData(wrapper: ElementWrapper) {
+        wrapper as ZoneNameWrapper
+        zone.text = wrapper.zoneName
+    }
+}
 
-        private fun applyRenderInfo(ri: RenderInfo) {
-            val context = itemView.context
-            val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
-
-            icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
-            icon.setImageTintList(fg)
-        }
+/**
+ * Holder for using with [ControlWrapper] to display names of zones.
+ * @param favoriteCallback this callback will be called whenever the favorite state of the
+ *                         [Control] this view represents changes.
+ */
+private class ControlHolder(view: View, val favoriteCallback: ModelFavoriteChanger) : Holder(view) {
+    private val icon: ImageView = itemView.requireViewById(R.id.icon)
+    private val title: TextView = itemView.requireViewById(R.id.title)
+    private val subtitle: TextView = itemView.requireViewById(R.id.subtitle)
+    private val removed: TextView = itemView.requireViewById(R.id.status)
+    private val favorite: CheckBox = itemView.requireViewById<CheckBox>(R.id.favorite).apply {
+        visibility = View.VISIBLE
     }
 
-    fun setItems(list: List<ControlStatus>) {
-        listOfControls = list
-        notifyDataSetChanged()
+    override fun bindData(wrapper: ElementWrapper) {
+        wrapper as ControlWrapper
+        val data = wrapper.controlStatus
+        val renderInfo = getRenderInfo(data.control.deviceType)
+        title.text = data.control.title
+        subtitle.text = data.control.subtitle
+        favorite.isChecked = data.favorite
+        removed.text = if (data.removed) "Removed" else ""
+        favorite.setOnClickListener {
+            favoriteCallback(data.control.controlId, favorite.isChecked)
+        }
+        applyRenderInfo(renderInfo)
+    }
+
+    private fun getRenderInfo(
+        @DeviceTypes.DeviceType deviceType: Int
+    ): RenderInfo {
+        return RenderInfo.lookup(deviceType, true)
+    }
+
+    private fun applyRenderInfo(ri: RenderInfo) {
+        val context = itemView.context
+        val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
+
+        icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
+        icon.setImageTintList(fg)
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index be52583..1e52371 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -18,10 +18,14 @@
 
 import android.app.Activity
 import android.content.ComponentName
+import android.content.Intent
 import android.os.Bundle
 import android.view.LayoutInflater
+import android.view.ViewStub
+import android.widget.Button
 import android.widget.TextView
 import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.ItemTouchHelper
 import androidx.recyclerview.widget.RecyclerView
 import com.android.systemui.R
 import com.android.systemui.broadcast.BroadcastDispatcher
@@ -41,13 +45,36 @@
     companion object {
         private const val TAG = "ControlsFavoritingActivity"
         const val EXTRA_APP = "extra_app_label"
-        const val EXTRA_COMPONENT = "extra_component"
     }
 
-    private lateinit var recyclerView: RecyclerView
-    private lateinit var adapter: ControlAdapter
+    private lateinit var recyclerViewAll: RecyclerView
+    private lateinit var adapterAll: ControlAdapter
+    private lateinit var recyclerViewFavorites: RecyclerView
+    private lateinit var adapterFavorites: ControlAdapter
     private var component: ComponentName? = null
 
+    private var currentModel: FavoriteModel? = null
+    private var itemTouchHelperCallback = object : ItemTouchHelper.SimpleCallback(
+            /* dragDirs */ ItemTouchHelper.UP
+                    or ItemTouchHelper.DOWN
+                    or ItemTouchHelper.LEFT
+                    or ItemTouchHelper.RIGHT,
+            /* swipeDirs */0
+    ) {
+        override fun onMove(
+            recyclerView: RecyclerView,
+            viewHolder: RecyclerView.ViewHolder,
+            target: RecyclerView.ViewHolder
+        ): Boolean {
+            return currentModel?.onMoveItem(
+                    viewHolder.adapterPosition, target.adapterPosition) != null
+        }
+
+        override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
+
+        override fun isItemViewSwipeEnabled() = false
+    }
+
     private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
         private val startingUser = controller.currentUserId
 
@@ -62,41 +89,77 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.controls_management)
+        requireViewById<ViewStub>(R.id.stub).apply {
+            layoutResource = R.layout.controls_management_favorites
+            inflate()
+        }
 
         val app = intent.getCharSequenceExtra(EXTRA_APP)
-        component = intent.getParcelableExtra<ComponentName>(EXTRA_COMPONENT)
+        component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
 
-        // If we have no component name, there's not much we can do.
-        val callback = component?.let {
-            { infoBuilder: ControlInfo.Builder, status: Boolean ->
-                infoBuilder.componentName = it
-                controller.changeFavoriteStatus(infoBuilder.build(), status)
-            }
-        } ?: { _, _ -> Unit }
-
-        recyclerView = requireViewById(R.id.list)
-        adapter = ControlAdapter(LayoutInflater.from(applicationContext), callback)
-        recyclerView.adapter = adapter
-        recyclerView.layoutManager = GridLayoutManager(applicationContext, 2)
-        val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin)
-        recyclerView.addItemDecoration(MarginItemDecorator(margin, margin))
+        setUpRecyclerViews()
 
         requireViewById<TextView>(R.id.title).text = app?.let { it }
                 ?: resources.getText(R.string.controls_favorite_default_title)
         requireViewById<TextView>(R.id.subtitle).text =
                 resources.getText(R.string.controls_favorite_subtitle)
 
-        currentUserTracker.startTracking()
-    }
+        requireViewById<Button>(R.id.done).setOnClickListener {
+            if (component == null) return@setOnClickListener
+            val favoritesForStorage = currentModel?.favorites?.map {
+                with(it.controlStatus.control) {
+                    ControlInfo(component!!, controlId, title, deviceType)
+                }
+            }
+            if (favoritesForStorage != null) {
+                controller.replaceFavoritesForComponent(component!!, favoritesForStorage)
+                finishAffinity()
+            }
+        }
 
-    override fun onResume() {
-        super.onResume()
         component?.let {
-            controller.loadForComponent(it) {
+            controller.loadForComponent(it) { allControls, favoriteKeys ->
                 executor.execute {
-                    adapter.setItems(it)
+                    val favoriteModel = FavoriteModel(
+                        allControls,
+                        favoriteKeys,
+                        allAdapter = adapterAll,
+                        favoritesAdapter = adapterFavorites)
+                    adapterAll.changeFavoritesModel(favoriteModel)
+                    adapterFavorites.changeFavoritesModel(favoriteModel)
+                    currentModel = favoriteModel
                 }
             }
         }
+
+        currentUserTracker.startTracking()
+    }
+
+    private fun setUpRecyclerViews() {
+        val margin = resources.getDimensionPixelSize(R.dimen.controls_card_margin)
+        val itemDecorator = MarginItemDecorator(margin, margin)
+        val layoutInflater = LayoutInflater.from(applicationContext)
+
+        adapterAll = ControlAdapter(layoutInflater)
+        recyclerViewAll = requireViewById<RecyclerView>(R.id.listAll).apply {
+            adapter = adapterAll
+            layoutManager = GridLayoutManager(applicationContext, 2).apply {
+                spanSizeLookup = adapterAll.spanSizeLookup
+            }
+            addItemDecoration(itemDecorator)
+        }
+
+        adapterFavorites = ControlAdapter(layoutInflater, true)
+        recyclerViewFavorites = requireViewById<RecyclerView>(R.id.listFavorites).apply {
+            layoutManager = GridLayoutManager(applicationContext, 2)
+            adapter = adapterFavorites
+            addItemDecoration(itemDecorator)
+        }
+        ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerViewFavorites)
+    }
+
+    override fun onDestroy() {
+        currentUserTracker.stopTracking()
+        super.onDestroy()
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index 645e929..ad4bdef 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -20,6 +20,7 @@
 import android.content.Intent
 import android.os.Bundle
 import android.view.LayoutInflater
+import android.view.ViewStub
 import android.widget.TextView
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
@@ -63,11 +64,21 @@
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setContentView(R.layout.controls_management)
+        requireViewById<ViewStub>(R.id.stub).apply {
+            layoutResource = R.layout.controls_management_apps
+            inflate()
+        }
 
         recyclerView = requireViewById(R.id.list)
-        recyclerView.adapter = AppAdapter(executor, lifecycle, listingController,
-                LayoutInflater.from(this), ::launchFavoritingActivity,
-                FavoritesRenderer(resources, controlsController::countFavoritesForComponent))
+        recyclerView.adapter = AppAdapter(
+                backExecutor,
+                executor,
+                lifecycle,
+                listingController,
+                LayoutInflater.from(this),
+                ::launchFavoritingActivity,
+                FavoritesRenderer(resources, controlsController::countFavoritesForComponent),
+                resources)
         recyclerView.layoutManager = LinearLayoutManager(applicationContext)
 
         requireViewById<TextView>(R.id.title).text =
@@ -89,11 +100,16 @@
                         .apply {
                     putExtra(ControlsFavoritingActivity.EXTRA_APP,
                             listingController.getAppLabel(it))
-                    putExtra(ControlsFavoritingActivity.EXTRA_COMPONENT, it)
-                    flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP
+                    putExtra(Intent.EXTRA_COMPONENT_NAME, it)
+                    flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
                 }
                 startActivity(intent)
             }
         }
     }
+
+    override fun onDestroy() {
+        currentUserTracker.stopTracking()
+        super.onDestroy()
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt
new file mode 100644
index 0000000..6bade0a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/FavoriteModel.kt
@@ -0,0 +1,153 @@
+/*
+ * 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.management
+
+import android.text.TextUtils
+import android.util.Log
+import com.android.systemui.controls.ControlStatus
+import java.util.Collections
+import java.util.Comparator
+
+/**
+ * Model for keeping track of current favorites and their order.
+ *
+ * This model is to be used with two [ControlAdapter] one that shows only favorites in the current
+ * order and another that shows all controls, separated by zone. When the favorite state of any
+ * control is modified or when the favorites are reordered, the adapters are notified of the change.
+ *
+ * @param listControls list of all the [ControlStatus] to display. This includes controls currently
+ *                     marked as favorites as well as those that have been removed (not returned
+ *                     from load)
+ * @param listFavoritesIds list of the [Control.controlId] for all the favorites, including those
+ *                         that have been removed.
+ * @param favoritesAdapter [ControlAdapter] used by the [RecyclerView] that shows only favorites
+ * @param allAdapter [ControlAdapter] used by the [RecyclerView] that shows all controls
+ */
+class FavoriteModel(
+    private val listControls: List<ControlStatus>,
+    listFavoritesIds: List<String>,
+    private val favoritesAdapter: ControlAdapter,
+    private val allAdapter: ControlAdapter
+) {
+
+    companion object {
+        private const val TAG = "FavoriteModel"
+    }
+
+    /**
+     * List of favorite controls ([ControlWrapper]) in order.
+     *
+     * Initially, this list will give a list of wrappers in the order specified by the constructor
+     * variable `listFavoriteIds`.
+     *
+     * As the favorites are added, removed or moved, this list will keep track of those changes.
+     */
+    val favorites: List<ControlWrapper> = listFavoritesIds.map { id ->
+            ControlWrapper(listControls.first { it.control.controlId == id })
+        }.toMutableList()
+
+    /**
+     * List of all controls by zones.
+     *
+     * Lists all the controls with the zone names interleaved as a flat list. After each zone name,
+     * the controls in that zone are listed. Zones are listed in alphabetical order
+     */
+    val all: List<ElementWrapper> = listControls.groupBy { it.control.zone }
+            .mapKeys { it.key ?: "" } // map null to empty
+            .toSortedMap(CharSequenceComparator())
+            .flatMap {
+                val controls = it.value.map { ControlWrapper(it) }
+                if (!TextUtils.isEmpty(it.key)) {
+                    listOf(ZoneNameWrapper(it.key)) + controls
+                } else {
+                    controls
+                }
+            }
+
+    /**
+     * Change the favorite status of a [Control].
+     *
+     * This can be invoked from any of the [ControlAdapter]. It will change the status of that
+     * control and either add it to the list of favorites (at the end) or remove it from it.
+     *
+     * Removing the favorite status from a Removed control will make it disappear completely if
+     * changes are saved.
+     *
+     * @param controlId the id of the [Control] to change the status
+     * @param favorite `true` if and only if it's set to be a favorite.
+     */
+    fun changeFavoriteStatus(controlId: String, favorite: Boolean) {
+        favorites as MutableList
+        val index = all.indexOfFirst {
+            it is ControlWrapper && it.controlStatus.control.controlId == controlId
+        }
+        val control = (all[index] as ControlWrapper).controlStatus
+        if (control.favorite == favorite) {
+            Log.d(TAG, "Changing favorite to same state for ${control.control.controlId} ")
+            return
+        } else {
+            control.favorite = favorite
+        }
+        allAdapter.notifyItemChanged(index)
+        if (favorite) {
+            favorites.add(all[index] as ControlWrapper)
+            favoritesAdapter.notifyItemInserted(favorites.size - 1)
+        } else {
+            val i = favorites.indexOfFirst { it.controlStatus.control.controlId == controlId }
+            favorites.removeAt(i)
+            favoritesAdapter.notifyItemRemoved(i)
+        }
+    }
+
+    /**
+     * Move items in the model and notify the [favoritesAdapter].
+     */
+    fun onMoveItem(from: Int, to: Int) {
+        if (from < to) {
+            for (i in from until to) {
+                Collections.swap(favorites, i, i + 1)
+            }
+        } else {
+            for (i in from downTo to + 1) {
+                Collections.swap(favorites, i, i - 1)
+            }
+        }
+        favoritesAdapter.notifyItemMoved(from, to)
+    }
+}
+
+/**
+ * Compares [CharSequence] as [String].
+ *
+ * It will have empty strings as the first element
+ */
+class CharSequenceComparator : Comparator<CharSequence> {
+    override fun compare(p0: CharSequence?, p1: CharSequence?): Int {
+        if (p0 == null && p1 == null) return 0
+        else if (p0 == null && p1 != null) return -1
+        else if (p0 != null && p1 == null) return 1
+        return p0.toString().compareTo(p1.toString())
+    }
+}
+
+/**
+ * Wrapper classes for the different types of elements shown in the [RecyclerView]s in
+ * [ControlsFavoritingActivity].
+ */
+sealed class ElementWrapper
+data class ZoneNameWrapper(val zoneName: CharSequence) : ElementWrapper()
+data class ControlWrapper(val controlStatus: ControlStatus) : ElementWrapper()
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 53a23b8..0ec739fe 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -30,6 +30,7 @@
 import android.view.LayoutInflater;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.NotificationMessagingUtil;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -191,6 +192,12 @@
         return new AlwaysOnDisplayPolicy(context);
     }
 
+    /***/
+    @Provides
+    public NotificationMessagingUtil provideNotificationMessagingUtil(Context context) {
+        return new NotificationMessagingUtil(context);
+    }
+
     /** */
     @Provides
     public ViewMediatorCallback providesViewMediatorCallback(KeyguardViewMediator viewMediator) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 7b54199..f068d9c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -40,6 +40,7 @@
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.people.PeopleHubModule;
+import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
 import com.android.systemui.statusbar.phone.KeyguardLiftController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -68,7 +69,9 @@
             NotificationsModule.class,
             PeopleHubModule.class,
         },
-        subcomponents = {StatusBarComponent.class, NotificationRowComponent.class})
+        subcomponents = {StatusBarComponent.class,
+                NotificationRowComponent.class,
+                ExpandableNotificationRowComponent.class})
 public abstract class SystemUIModule {
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index b3fc027..a3cd5fd 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -1045,7 +1045,9 @@
             Action action = getItem(position);
             View view = action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
             view.setOnClickListener(v -> onClickItem(position));
-            view.setOnLongClickListener(v -> onLongClickItem(position));
+            if (action instanceof LongPressAction) {
+                view.setOnLongClickListener(v -> onLongClickItem(position));
+            }
             return view;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
index 657a308..11e215d 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
@@ -153,11 +153,11 @@
         return true;
     }
 
-    private boolean checkExtensionCapability(String extName) {
+    boolean checkExtensionCapability(String extName) {
         return mExts.contains(extName);
     }
 
-    private int getWcgCapability() {
+    int getWcgCapability() {
         if (checkExtensionCapability(EXT_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH)) {
             return EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT;
         }
@@ -212,7 +212,7 @@
             if (wcg && checkExtensionCapability(KHR_GL_COLOR_SPACE) && wcgCapability > 0) {
                 attrs = new int[] {EGL_GL_COLORSPACE_KHR, wcgCapability, EGL_NONE};
             }
-            mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, attrs, 0);
+            mEglSurface = askCreatingEglWindowSurface(surfaceHolder, attrs, 0 /* offset */);
         } else {
             Log.w(TAG, "Create EglSurface failed: hasEglDisplay=" + hasEglDisplay()
                     + ", has valid surface=" + surfaceHolder.getSurface().isValid());
@@ -235,6 +235,10 @@
         return true;
     }
 
+    EGLSurface askCreatingEglWindowSurface(SurfaceHolder holder, int[] attrs, int offset) {
+        return eglCreateWindowSurface(mEglDisplay, mEglConfig, holder, attrs, offset);
+    }
+
     /**
      * Destroy EglSurface.
      */
@@ -242,7 +246,7 @@
         if (hasEglSurface()) {
             eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
             eglDestroySurface(mEglDisplay, mEglSurface);
-            mEglSurface = null;
+            mEglSurface = EGL_NO_SURFACE;
         }
     }
 
@@ -296,7 +300,7 @@
     public void destroyEglContext() {
         if (hasEglContext()) {
             eglDestroyContext(mEglDisplay, mEglContext);
-            mEglContext = null;
+            mEglContext = EGL_NO_CONTEXT;
         }
     }
 
@@ -340,11 +344,16 @@
             destroyEglContext();
         }
         if (hasEglDisplay()) {
-            eglTerminate(mEglDisplay);
+            terminateEglDisplay();
         }
         mEglReady = false;
     }
 
+    void terminateEglDisplay() {
+        eglTerminate(mEglDisplay);
+        mEglDisplay = EGL_NO_DISPLAY;
+    }
+
     /**
      * Called to dump current state.
      * @param prefix prefix.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
index ae380b7..6498b91 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WorkLockActivityController.java
@@ -93,7 +93,6 @@
             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/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
index f9b18cf..c7bfc06 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -280,7 +280,9 @@
         if (mToActivityMessenger != null) {
             Bundle data = new Bundle();
             data.putInt(EXTRA_MENU_STATE, menuState);
-            data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds);
+            if (stackBounds != null) {
+                data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds);
+            }
             data.putParcelable(EXTRA_MOVEMENT_BOUNDS, movementBounds);
             data.putBoolean(EXTRA_ALLOW_TIMEOUT, allowMenuTimeout);
             data.putBoolean(EXTRA_WILL_RESIZE_MENU, willResizeMenu);
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 ae61622..c118630f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -191,6 +191,7 @@
         mTile.setLabel(tile.getLabel());
         mTile.setSubtitle(tile.getSubtitle());
         mTile.setContentDescription(tile.getContentDescription());
+        mTile.setStateDescription(tile.getStateDescription());
         mTile.setState(tile.getState());
     }
 
@@ -345,6 +346,12 @@
             state.contentDescription = state.label;
         }
 
+        if (mTile.getStateDescription() != null) {
+            state.stateDescription = mTile.getStateDescription();
+        } else {
+            state.stateDescription = null;
+        }
+
         if (state instanceof BooleanState) {
             state.expandedAccessibilityClassName = Switch.class.getName();
             ((BooleanState) state).value = (state.state == Tile.STATE_ACTIVE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 2fe64d2..8feee10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -63,7 +63,6 @@
     private String mAccessibilityClass;
     private boolean mTileState;
     private boolean mCollapsedView;
-    private boolean mClicked;
     private boolean mShowRippleEffect = true;
 
     private final ImageView mBg;
@@ -230,13 +229,35 @@
         setLongClickable(state.handlesLongClick);
         mIcon.setIcon(state, allowAnimations);
         setContentDescription(state.contentDescription);
+        final StringBuilder stateDescription = new StringBuilder();
+        switch (state.state) {
+            case Tile.STATE_UNAVAILABLE:
+                stateDescription.append(mContext.getString(R.string.tile_unavailable));
+                break;
+            case Tile.STATE_INACTIVE:
+                if (state instanceof QSTile.BooleanState) {
+                    stateDescription.append(mContext.getString(R.string.switch_bar_off));
+                }
+                break;
+            case Tile.STATE_ACTIVE:
+                if (state instanceof QSTile.BooleanState) {
+                    stateDescription.append(mContext.getString(R.string.switch_bar_on));
+                }
+                break;
+            default:
+                break;
+        }
+        if (!TextUtils.isEmpty(state.stateDescription)) {
+            stateDescription.append(", ");
+            stateDescription.append(state.stateDescription);
+        }
+        setStateDescription(stateDescription.toString());
 
         mAccessibilityClass =
                 state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName;
         if (state instanceof QSTile.BooleanState) {
             boolean newState = ((BooleanState) state).value;
             if (mTileState != newState) {
-                mClicked = false;
                 mTileState = newState;
             }
         }
@@ -288,23 +309,10 @@
     }
 
     @Override
-    public boolean performClick() {
-        mClicked = true;
-        return super.performClick();
-    }
-
-    @Override
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
         super.onInitializeAccessibilityEvent(event);
         if (!TextUtils.isEmpty(mAccessibilityClass)) {
             event.setClassName(mAccessibilityClass);
-            if (Switch.class.getName().equals(mAccessibilityClass)) {
-                boolean b = mClicked ? !mTileState : mTileState;
-                String label = getResources()
-                        .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
-                event.setContentDescription(label);
-                event.setChecked(b);
-            }
         }
     }
 
@@ -316,11 +324,13 @@
         if (!TextUtils.isEmpty(mAccessibilityClass)) {
             info.setClassName(mAccessibilityClass);
             if (Switch.class.getName().equals(mAccessibilityClass)) {
-                boolean b = mClicked ? !mTileState : mTileState;
-                String label = getResources()
-                        .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
+                String label = getResources().getString(
+                        mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
+                // Set the text here for tests in
+                // android.platform.test.scenario.sysui.quicksettings. Can be removed when
+                // UiObject2 has a new getStateDescription() API and tests are updated.
                 info.setText(label);
-                info.setChecked(b);
+                info.setChecked(mTileState);
                 info.setCheckable(true);
                 if (isLongClickable()) {
                     info.addAction(
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 e1b61c6..e559694 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -109,10 +109,31 @@
 
     private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
 
+    /**
+     * Provides a new {@link TState} of the appropriate type to use between this tile and the
+     * corresponding view.
+     *
+     * @return new state to use by the tile.
+     */
     public abstract TState newTileState();
 
+    /**
+     * Handles clicks by the user.
+     *
+     * Calls to the controller should be made here to set the new state of the device.
+     */
     abstract protected void handleClick();
 
+    /**
+     * Update state of the tile based on device state
+     *
+     * Called whenever the state of the tile needs to be updated, either after user
+     * interaction or from callbacks from the controller. It populates {@code state} with the
+     * information to display to the user.
+     *
+     * @param state {@link TState} to populate with information to display
+     * @param arg additional arguments needed to populate {@code state}
+     */
     abstract protected void handleUpdateState(TState state, Object arg);
 
     /**
@@ -177,6 +198,12 @@
         return mHost;
     }
 
+    /**
+     * Return the {@link QSIconView} to be used by this tile's view.
+     *
+     * @param context view context for the view
+     * @return icon view for this tile
+     */
     public QSIconView createTileView(Context context) {
         return new QSIconViewImpl(context);
     }
@@ -298,11 +325,20 @@
         mCallbacks.clear();
     }
 
+    /**
+     * Handles secondary click on the tile.
+     *
+     * Defaults to {@link QSTileImpl#handleClick}
+     */
     protected void handleSecondaryClick() {
         // Default to normal click.
         handleClick();
     }
 
+    /**
+     * Handles long click on the tile by launching the {@link Intent} defined in
+     * {@link QSTileImpl#getLongClickIntent}
+     */
     protected void handleLongClick() {
         if (mQSSettingsPanelOption == QSSettingsPanel.USE_DETAIL) {
             showDetail(true);
@@ -312,6 +348,11 @@
                 getLongClickIntent(), 0);
     }
 
+    /**
+     * Returns an intent to be launched when the tile is long pressed.
+     *
+     * @return the intent to launch
+     */
     public abstract Intent getLongClickIntent();
 
     protected void handleRefreshState(Object arg) {
@@ -427,6 +468,10 @@
         }
     }
 
+    /**
+     * Provides a default label for the tile.
+     * @return default label for the tile.
+     */
     public abstract CharSequence getTileLabel();
 
     public static int getColorForState(Context context, int state) {
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 9282a2e..361b6c1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -134,25 +134,27 @@
         state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
         state.secondaryLabel = TextUtils.emptyIfNull(
                 getSecondaryLabel(enabled, connecting, connected, state.isTransient));
+        state.contentDescription = state.label;
+        state.stateDescription = "";
         if (enabled) {
             if (connected) {
                 state.icon = new BluetoothConnectedTileIcon();
                 if (!TextUtils.isEmpty(mController.getConnectedDeviceName())) {
                     state.label = mController.getConnectedDeviceName();
                 }
-                state.contentDescription =
+                state.stateDescription =
                         mContext.getString(R.string.accessibility_bluetooth_name, state.label)
                                 + ", " + state.secondaryLabel;
             } else if (state.isTransient) {
                 state.icon = ResourceIcon.get(
                         com.android.internal.R.drawable.ic_bluetooth_transient_animation);
-                state.contentDescription = state.secondaryLabel;
+                state.stateDescription = state.secondaryLabel;
             } else {
                 state.icon =
                         ResourceIcon.get(com.android.internal.R.drawable.ic_qs_bluetooth);
                 state.contentDescription = mContext.getString(
-                        R.string.accessibility_quick_settings_bluetooth) + ","
-                        + mContext.getString(R.string.accessibility_not_connected);
+                        R.string.accessibility_quick_settings_bluetooth);
+                state.stateDescription = mContext.getString(R.string.accessibility_not_connected);
             }
             state.state = Tile.STATE_ACTIVE;
         } else {
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 32b051e..58de057 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -183,6 +183,7 @@
     protected void handleUpdateState(BooleanState state, Object arg) {
         state.label = mContext.getString(R.string.quick_settings_cast_title);
         state.contentDescription = state.label;
+        state.stateDescription = "";
         state.value = false;
         final List<CastDevice> devices = mController.getCastDevices();
         boolean connecting = false;
@@ -192,8 +193,9 @@
             if (device.state == CastDevice.STATE_CONNECTED) {
                 state.value = true;
                 state.secondaryLabel = getDeviceName(device);
-                state.contentDescription = state.contentDescription + ","
-                        + mContext.getString(R.string.accessibility_cast_name, state.label);
+                state.stateDescription = state.stateDescription + ","
+                        + mContext.getString(
+                                R.string.accessibility_cast_name, state.label);
                 connecting = false;
                 break;
             } else if (device.state == CastDevice.STATE_CONNECTING) {
@@ -217,9 +219,8 @@
             state.state = Tile.STATE_UNAVAILABLE;
             String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi);
             state.secondaryLabel = noWifi;
-            state.contentDescription = state.contentDescription + ", " + mContext.getString(
-                    R.string.accessibility_quick_settings_not_available, noWifi);
         }
+        state.stateDescription = state.stateDescription + ", " + state.secondaryLabel;
         mDetailAdapter.updateItems(devices);
     }
 
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 22470c7..d5f86c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -194,17 +194,13 @@
             state.secondaryLabel = r.getString(R.string.cell_data_off);
         }
 
-
-        // TODO(b/77881974): Instead of switching out the description via a string check for
-        // we need to have two strings provided by the MobileIconGroup.
-        final CharSequence contentDescriptionSuffix;
+        state.contentDescription = state.label;
         if (state.state == Tile.STATE_INACTIVE) {
-            contentDescriptionSuffix = r.getString(R.string.cell_data_off_content_description);
+            // This information is appended later by converting the Tile.STATE_INACTIVE state.
+            state.stateDescription = "";
         } else {
-            contentDescriptionSuffix = state.secondaryLabel;
+            state.stateDescription = state.secondaryLabel;
         }
-
-        state.contentDescription = state.label + ", " + contentDescriptionSuffix;
     }
 
     private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
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 52d1a5b3..9215da4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -240,6 +240,8 @@
                 zen != Global.ZEN_MODE_OFF, mController.getConfig(), false));
         state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_dnd);
         checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
+        // Keeping the secondaryLabel in contentDescription instead of stateDescription is easier
+        // to understand.
         switch (zen) {
             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
                 state.contentDescription =
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 dafdd89..792c364 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -102,14 +102,13 @@
         }
         state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
         state.secondaryLabel = "";
+        state.stateDescription = "";
         if (!mFlashlightController.isAvailable()) {
             state.icon = mIcon;
             state.slash.isSlashed = true;
             state.secondaryLabel = mContext.getString(
                     R.string.quick_settings_flashlight_camera_in_use);
-            state.contentDescription = mContext.getString(
-                    R.string.accessibility_quick_settings_flashlight_unavailable)
-                    + ", " + state.secondaryLabel;
+            state.stateDescription = state.secondaryLabel;
             state.state = Tile.STATE_UNAVAILABLE;
             return;
         }
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 42f8010..91b3ae4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -148,6 +148,7 @@
 
         state.secondaryLabel = getSecondaryLabel(
                 isTileActive, isTransient, isDataSaverEnabled, numConnectedDevices);
+        state.stateDescription = state.secondaryLabel;
     }
 
     @Nullable
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 fbdca3b..e617867 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -105,15 +105,8 @@
         }
         state.icon = mIcon;
         state.slash.isSlashed = !state.value;
-        if (locationEnabled) {
-            state.label = mContext.getString(R.string.quick_settings_location_label);
-            state.contentDescription = mContext.getString(
-                    R.string.accessibility_quick_settings_location_on);
-        } else {
-            state.label = mContext.getString(R.string.quick_settings_location_label);
-            state.contentDescription = mContext.getString(
-                    R.string.accessibility_quick_settings_location_off);
-        }
+        state.label = mContext.getString(R.string.quick_settings_location_label);
+        state.contentDescription = state.label;
         state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
         state.expandedAccessibilityClassName = Switch.class.getName();
     }
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 b7ce101..6e8dcf3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -195,6 +195,7 @@
         state.activityIn = cb.enabled && cb.activityIn;
         state.activityOut = cb.enabled && cb.activityOut;
         final StringBuffer minimalContentDescription = new StringBuffer();
+        final StringBuffer minimalStateDescription = new StringBuffer();
         final Resources r = mContext.getResources();
         if (isTransient) {
             state.icon = ResourceIcon.get(
@@ -219,13 +220,14 @@
                 mContext.getString(R.string.quick_settings_wifi_label)).append(",");
         if (state.value) {
             if (wifiConnected) {
-                minimalContentDescription.append(cb.wifiSignalContentDescription).append(",");
+                minimalStateDescription.append(cb.wifiSignalContentDescription);
                 minimalContentDescription.append(removeDoubleQuotes(cb.ssid));
                 if (!TextUtils.isEmpty(state.secondaryLabel)) {
                     minimalContentDescription.append(",").append(state.secondaryLabel);
                 }
             }
         }
+        state.stateDescription = minimalStateDescription.toString();
         state.contentDescription = minimalContentDescription.toString();
         state.dualLabelContentDescription = r.getString(
                 R.string.accessibility_quick_settings_open_settings, getTileLabel());
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 7853dc3..e54ee51 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -103,14 +103,11 @@
         state.icon = mIcon;
         if (state.value) {
             state.slash.isSlashed = false;
-            state.contentDescription =  mContext.getString(
-                    R.string.accessibility_quick_settings_work_mode_on);
         } else {
             state.slash.isSlashed = true;
-            state.contentDescription =  mContext.getString(
-                    R.string.accessibility_quick_settings_work_mode_off);
         }
         state.label = mContext.getString(R.string.quick_settings_work_mode_label);
+        state.contentDescription = state.label;
         state.expandedAccessibilityClassName = Switch.class.getName();
         state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 880b8f8..4f38a15 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -69,6 +69,7 @@
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.animation.Interpolator;
+import android.widget.FrameLayout;
 import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -143,7 +144,7 @@
     private static final float BACKGROUND_ALPHA = 0.5f;
     private static final float SCREENSHOT_DROP_IN_MIN_SCALE = 0.725f;
     private static final float ROUNDED_CORNER_RADIUS = .05f;
-    private static final long SCREENSHOT_CORNER_TIMEOUT_MILLIS = 8000;
+    private static final long SCREENSHOT_CORNER_TIMEOUT_MILLIS = 6000;
     private static final int MESSAGE_CORNER_TIMEOUT = 2;
 
     private final ScreenshotNotificationsController mNotificationsController;
@@ -162,6 +163,7 @@
     private final HorizontalScrollView mActionsContainer;
     private final LinearLayout mActionsView;
     private final ImageView mBackgroundProtection;
+    private final FrameLayout mDismissButton;
 
     private Bitmap mScreenBitmap;
     private AnimatorSet mScreenshotAnimation;
@@ -170,6 +172,7 @@
     private float mScreenshotOffsetYPx;
     private float mScreenshotHeightPx;
     private float mCornerScale;
+    private float mDismissButtonSize;
 
     private AsyncTask<Void, Void, Void> mSaveInBgTask;
 
@@ -216,19 +219,14 @@
         mActionsView = mScreenshotLayout.findViewById(R.id.global_screenshot_actions);
         mBackgroundProtection = mScreenshotLayout.findViewById(
                 R.id.global_screenshot_actions_background);
+        mDismissButton = mScreenshotLayout.findViewById(R.id.global_screenshot_dismiss_button);
+        mDismissButton.setOnClickListener(view -> clearScreenshot("dismiss_button"));
 
         mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_flash);
         mScreenshotSelectorView = mScreenshotLayout.findViewById(R.id.global_screenshot_selector);
         mScreenshotLayout.setFocusable(true);
         mScreenshotSelectorView.setFocusable(true);
         mScreenshotSelectorView.setFocusableInTouchMode(true);
-        mScreenshotLayout.setOnTouchListener((v, event) -> {
-            if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
-                clearScreenshot("tap_outside");
-            }
-            // Intercept and ignore all touch events
-            return true;
-        });
 
         // Setup the window that we are going to use
         mWindowLayoutParams = new WindowManager.LayoutParams(
@@ -254,6 +252,8 @@
                 resources.getDimensionPixelSize(R.dimen.screenshot_action_container_offset_y);
         mCornerScale = resources.getDimensionPixelSize(R.dimen.global_screenshot_x_scale)
                 / (float) mDisplayMetrics.widthPixels;
+        mDismissButtonSize = resources.getDimensionPixelSize(
+                R.dimen.screenshot_dismiss_button_tappable_size);
 
         // Setup the Camera shutter sound
         mCameraSound = new MediaActionSound();
@@ -271,6 +271,9 @@
         Rect actionsRect = new Rect();
         mActionsContainer.getBoundsOnScreen(actionsRect);
         touchRegion.op(actionsRect, Region.Op.UNION);
+        Rect dismissRect = new Rect();
+        mDismissButton.getBoundsOnScreen(dismissRect);
+        touchRegion.op(dismissRect, Region.Op.UNION);
 
         inoutInfo.touchableRegion.set(touchRegion);
     }
@@ -408,6 +411,7 @@
         mActionsContainer.setVisibility(View.GONE);
         mBackgroundView.setVisibility(View.GONE);
         mBackgroundProtection.setAlpha(0f);
+        mDismissButton.setVisibility(View.GONE);
         mScreenshotView.setVisibility(View.GONE);
         mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
     }
@@ -615,6 +619,17 @@
             mScreenshotView.setTranslationX(t * finalPos.x);
             mScreenshotView.setTranslationY(t * finalPos.y);
         });
+        anim.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                Rect bounds = new Rect();
+                mScreenshotView.getBoundsOnScreen(bounds);
+                mDismissButton.setX(bounds.right - mDismissButtonSize / 2f);
+                mDismissButton.setY(bounds.top - mDismissButtonSize / 2f);
+                mDismissButton.setVisibility(View.VISIBLE);
+            }
+        });
         return anim;
     }
 
@@ -686,14 +701,6 @@
             mBackgroundProtection.setAlpha(t);
             mActionsContainer.setY(mDisplayMetrics.heightPixels - actionsViewHeight * t);
         });
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                mScreenshotView.requestFocus();
-                mScreenshotView.setElevation(50);
-            }
-        });
         return animator;
     }
 
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 41c1b7b..006d40d 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
@@ -69,6 +69,7 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
 import com.android.systemui.statusbar.notification.row.NotificationGuts;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager;
@@ -153,6 +154,7 @@
 
     private NotificationEntry parent; // our parent (if we're in a group)
     private ExpandableNotificationRow row; // the outer expanded view
+    private ExpandableNotificationRowController mRowController;
 
     private int mCachedContrastColor = COLOR_INVALID;
     private int mCachedContrastColorIsFor = COLOR_INVALID;
@@ -424,6 +426,14 @@
         this.row = row;
     }
 
+    public ExpandableNotificationRowController getRowController() {
+        return mRowController;
+    }
+
+    public void setRowController(ExpandableNotificationRowController controller) {
+        mRowController = controller;
+    }
+
     @Nullable
     public List<NotificationEntry> getChildren() {
         if (row == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index ecf62db..e8a62e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar.notification.collection.inflation;
 
 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
-import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
 
 import android.annotation.Nullable;
@@ -40,19 +39,19 @@
 import com.android.systemui.statusbar.notification.NotificationClicker;
 import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
 import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
 import com.android.systemui.statusbar.notification.row.RowContentBindParams;
 import com.android.systemui.statusbar.notification.row.RowContentBindStage;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
+import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 import java.util.Objects;
 
@@ -67,35 +66,28 @@
 
     private static final String TAG = "NotificationViewManager";
 
-    private final NotificationGroupManager mGroupManager;
-    private final NotificationGutsManager mGutsManager;
     private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider;
 
     private final Context mContext;
     private final NotifBindPipeline mNotifBindPipeline;
     private final RowContentBindStage mRowContentBindStage;
     private final NotificationMessagingUtil mMessagingUtil;
-    private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
-            this::logNotificationExpansion;
     private final NotificationRemoteInputManager mNotificationRemoteInputManager;
     private final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
-    private final boolean mAllowLongPress;
-    private final KeyguardBypassController mKeyguardBypassController;
-    private final StatusBarStateController mStatusBarStateController;
 
     private NotificationPresenter mPresenter;
     private NotificationListContainer mListContainer;
-    private HeadsUpManager mHeadsUpManager;
     private NotificationRowContentBinder.InflationCallback mInflationCallback;
-    private ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
     private BindRowCallback mBindRowCallback;
     private NotificationClicker mNotificationClicker;
     private final Provider<RowInflaterTask> mRowInflaterTaskProvider;
-    private final NotificationLogger mNotificationLogger;
+    private final ExpandableNotificationRowComponent.Builder
+            mExpandableNotificationRowComponentBuilder;
 
     @Inject
     public NotificationRowBinderImpl(
             Context context,
+            NotificationMessagingUtil notificationMessagingUtil,
             NotificationRemoteInputManager notificationRemoteInputManager,
             NotificationLockscreenUserManager notificationLockscreenUserManager,
             NotifBindPipeline notifBindPipeline,
@@ -107,21 +99,16 @@
             NotificationGutsManager notificationGutsManager,
             NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             Provider<RowInflaterTask> rowInflaterTaskProvider,
-            NotificationLogger logger) {
+            ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder) {
         mContext = context;
         mNotifBindPipeline = notifBindPipeline;
         mRowContentBindStage = rowContentBindStage;
-        mMessagingUtil = new NotificationMessagingUtil(context);
+        mMessagingUtil = notificationMessagingUtil;
         mNotificationRemoteInputManager = notificationRemoteInputManager;
         mNotificationLockscreenUserManager = notificationLockscreenUserManager;
-        mAllowLongPress = allowLongPress;
-        mKeyguardBypassController = keyguardBypassController;
-        mStatusBarStateController = statusBarStateController;
-        mGroupManager = notificationGroupManager;
-        mGutsManager = notificationGutsManager;
         mNotificationInterruptionStateProvider = notificationInterruptionStateProvider;
         mRowInflaterTaskProvider = rowInflaterTaskProvider;
-        mNotificationLogger = logger;
+        mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder;
     }
 
     /**
@@ -129,13 +116,10 @@
      */
     public void setUpWithPresenter(NotificationPresenter presenter,
             NotificationListContainer listContainer,
-            HeadsUpManager headsUpManager,
             BindRowCallback bindRowCallback) {
         mPresenter = presenter;
         mListContainer = listContainer;
-        mHeadsUpManager = headsUpManager;
         mBindRowCallback = bindRowCallback;
-        mOnAppOpsClickListener = mGutsManager::openGuts;
     }
 
     public void setInflationCallback(NotificationRowContentBinder.InflationCallback callback) {
@@ -150,9 +134,7 @@
      * Inflates the views for the given entry (possibly asynchronously).
      */
     @Override
-    public void inflateViews(
-            NotificationEntry entry,
-            Runnable onDismissRunnable)
+    public void inflateViews(NotificationEntry entry, Runnable onDismissRunnable)
             throws InflationException {
         ViewGroup parent = mListContainer.getViewParentForNotification(entry);
         PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
@@ -163,12 +145,26 @@
             entry.updateIcons(mContext, sbn);
             entry.reset();
             updateNotification(entry, pmUser, sbn, entry.getRow());
-            entry.getRow().setOnDismissRunnable(onDismissRunnable);
+            entry.getRowController().setOnDismissRunnable(onDismissRunnable);
         } else {
             entry.createIcons(mContext, sbn);
             mRowInflaterTaskProvider.get().inflate(mContext, parent, entry,
                     row -> {
-                        bindRow(entry, pmUser, sbn, row, onDismissRunnable);
+                        // Setup the controller for the view.
+                        ExpandableNotificationRowComponent component =
+                                mExpandableNotificationRowComponentBuilder
+                                        .expandableNotificationRow(row)
+                                        .notificationEntry(entry)
+                                        .onDismissRunnable(onDismissRunnable)
+                                        .inflationCallback(mInflationCallback)
+                                        .rowContentBindStage(mRowContentBindStage)
+                                        .onExpandClickListener(mPresenter)
+                                        .build();
+                        ExpandableNotificationRowController rowController =
+                                component.getExpandableNotificationRowController();
+                        rowController.init();
+                        entry.setRowController(rowController);
+                        bindRow(entry, pmUser, sbn, row);
                         updateNotification(entry, pmUser, sbn, row);
                     });
         }
@@ -176,55 +172,12 @@
 
     //TODO: This method associates a row with an entry, but eventually needs to not do that
     private void bindRow(NotificationEntry entry, PackageManager pmUser,
-            StatusBarNotification sbn, ExpandableNotificationRow row,
-            Runnable onDismissRunnable) {
-        // Get the app name.
-        // Note that Notification.Builder#bindHeaderAppName has similar logic
-        // but since this field is used in the guts, it must be accurate.
-        // Therefore we will only show the application label, or, failing that, the
-        // package name. No substitutions.
-        final String pkg = sbn.getPackageName();
-        String appname = pkg;
-        try {
-            final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
-                    PackageManager.MATCH_UNINSTALLED_PACKAGES
-                            | PackageManager.MATCH_DISABLED_COMPONENTS);
-            if (info != null) {
-                appname = String.valueOf(pmUser.getApplicationLabel(info));
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            // Do nothing
-        }
-
-        row.initialize(
-                appname,
-                sbn.getKey(),
-                mExpansionLogger,
-                mKeyguardBypassController,
-                mGroupManager,
-                mHeadsUpManager,
-                mRowContentBindStage,
-                mPresenter);
-
-        // TODO: Either move these into ExpandableNotificationRow#initialize or out of row entirely
-        row.setStatusBarStateController(mStatusBarStateController);
-        row.setAppOpsOnClickListener(mOnAppOpsClickListener);
-        if (mAllowLongPress) {
-            row.setLongPressListener(mGutsManager::openGuts);
-        }
+            StatusBarNotification sbn, ExpandableNotificationRow row) {
         mListContainer.bindRow(row);
         mNotificationRemoteInputManager.bindRow(row);
-
-        row.setOnDismissRunnable(onDismissRunnable);
-        row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-        if (ENABLE_REMOTE_INPUT) {
-            row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
-        }
-
         entry.setRow(row);
         row.setEntry(entry);
         mNotifBindPipeline.manageRow(entry, row);
-
         mBindRowCallback.onBindRow(entry, pmUser, sbn, row);
     }
 
@@ -307,10 +260,6 @@
         Objects.requireNonNull(mNotificationClicker).register(row, sbn);
     }
 
-    private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
-        mNotificationLogger.onExpansionChanged(key, userAction, expanded);
-    }
-
     /** Callback for when a row is bound to an entry. */
     public interface BindRowCallback {
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 254b64f..3e0bcbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -90,7 +90,6 @@
         notificationRowBinder.setUpWithPresenter(
                 presenter,
                 listContainer,
-                headsUpManager,
                 bindRowCallback)
 
         if (featureFlags.isNewNotifPipelineEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 50a2037..3eac229 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -33,16 +33,14 @@
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
 
-import com.android.systemui.Dependency;
+import com.android.systemui.Gefingerpoken;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.FakeShadowView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
-import com.android.systemui.statusbar.phone.DoubleTapHelper;
 
 /**
  * Base class for both {@link ExpandableNotificationRow} and {@link NotificationShelf}
@@ -94,14 +92,12 @@
     private static final Interpolator ACTIVATE_INVERSE_ALPHA_INTERPOLATOR
             = new PathInterpolator(0, 0, 0.5f, 1);
     private int mTintedRippleColor;
-    protected int mNormalRippleColor;
-    private final AccessibilityManager mAccessibilityManager;
-    private final DoubleTapHelper mDoubleTapHelper;
+    private int mNormalRippleColor;
+    private Gefingerpoken mTouchHandler;
 
     private boolean mDimmed;
 
-    protected int mBgTint = NO_COLOR;
-    private float mBgAlpha = 1f;
+    int mBgTint = NO_COLOR;
 
     /**
      * Flag to indicate that the notification has been touched once and the second touch will
@@ -116,7 +112,7 @@
     private Interpolator mCurrentAppearInterpolator;
     private Interpolator mCurrentAlphaInterpolator;
 
-    protected NotificationBackgroundView mBackgroundNormal;
+    NotificationBackgroundView mBackgroundNormal;
     private NotificationBackgroundView mBackgroundDimmed;
     private ObjectAnimator mBackgroundAnimator;
     private RectF mAppearAnimationRect = new RectF();
@@ -130,7 +126,6 @@
     private boolean mLastInSection;
     private boolean mFirstInSection;
     private boolean mIsBelowSpeedBump;
-    private final FalsingManager mFalsingManager;
 
     private float mNormalBackgroundVisibilityAmount;
     private float mDimmedBackgroundFadeInAmount = -1;
@@ -154,38 +149,25 @@
      */
     private boolean mNeedsDimming;
     private int mDimmedAlpha;
-    private boolean mBlockNextTouch;
     private boolean mIsHeadsUpAnimation;
     private int mHeadsUpAddStartLocation;
     private float mHeadsUpLocation;
     private boolean mIsAppearing;
     private boolean mDismissed;
     private boolean mRefocusOnDismiss;
+    private OnDimmedListener mOnDimmedListener;
+    private AccessibilityManager mAccessibilityManager;
 
     public ActivatableNotificationView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mSlowOutFastInInterpolator = new PathInterpolator(0.8f, 0.0f, 0.6f, 1.0f);
         mSlowOutLinearInInterpolator = new PathInterpolator(0.8f, 0.0f, 1.0f, 1.0f);
-        mFalsingManager = Dependency.get(FalsingManager.class);  // TODO: inject into a controller.
         setClipChildren(false);
         setClipToPadding(false);
         updateColors();
-        mAccessibilityManager = AccessibilityManager.getInstance(mContext);
-
-        mDoubleTapHelper = new DoubleTapHelper(this, (active) -> {
-            if (active) {
-                makeActive();
-            } else {
-                makeInactive(true /* animate */);
-            }
-        }, super::performClick, this::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
         initDimens();
     }
 
-    public FalsingManager getFalsingManager() {
-        return mFalsingManager;
-    }
-
     private void updateColors() {
         mNormalColor = mContext.getColor(R.color.notification_material_background_color);
         mTintedRippleColor = mContext.getColor(
@@ -236,32 +218,15 @@
         mBackgroundDimmed.setCustomBackground(R.drawable.notification_material_bg_dim);
     }
 
-    private final Runnable mTapTimeoutRunnable = new Runnable() {
-        @Override
-        public void run() {
-            makeInactive(true /* animate */);
-        }
-    };
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (mNeedsDimming && ev.getActionMasked() == MotionEvent.ACTION_DOWN
-                && disallowSingleClick(ev) && !isTouchExplorationEnabled()) {
-            if (!mActivated) {
-                return true;
-            } else if (!mDoubleTapHelper.isWithinDoubleTapSlop(ev)) {
-                mBlockNextTouch = true;
-                makeInactive(true /* animate */);
-                return true;
-            }
+        if (mTouchHandler != null && mTouchHandler.onInterceptTouchEvent(ev)) {
+            return true;
         }
         return super.onInterceptTouchEvent(ev);
     }
 
-    private boolean isTouchExplorationEnabled() {
-        return mAccessibilityManager.isTouchExplorationEnabled();
-    }
-
     protected boolean disallowSingleClick(MotionEvent ev) {
         return false;
     }
@@ -270,25 +235,6 @@
         return false;
     }
 
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        boolean result;
-        if (mBlockNextTouch) {
-            mBlockNextTouch = false;
-            return false;
-        }
-        if (mNeedsDimming && !isTouchExplorationEnabled() && isInteractive()) {
-            boolean wasActivated = mActivated;
-            result = handleTouchEventDimmed(event);
-            if (wasActivated && result && event.getAction() == MotionEvent.ACTION_UP) {
-                removeCallbacks(mTapTimeoutRunnable);
-            }
-        } else {
-            result = super.onTouchEvent(event);
-        }
-        return result;
-    }
-
     /**
      * @return whether this view is interactive and can be double tapped
      */
@@ -313,28 +259,11 @@
         }
     }
 
-    public void setRippleAllowed(boolean allowed) {
+    void setRippleAllowed(boolean allowed) {
         mBackgroundNormal.setPressedAllowed(allowed);
     }
 
-    private boolean handleTouchEventDimmed(MotionEvent event) {
-        if (mNeedsDimming && !mDimmed) {
-            // We're actually dimmed, but our content isn't dimmable, let's ensure we have a ripple
-            super.onTouchEvent(event);
-        }
-        return mDoubleTapHelper.onTouchEvent(event, getActualHeight());
-    }
-
-    @Override
-    public boolean performClick() {
-        if (!mNeedsDimming || isTouchExplorationEnabled()) {
-            return super.performClick();
-        }
-        return false;
-    }
-
-    private void makeActive() {
-        mFalsingManager.onNotificationActive();
+    void makeActive() {
         startActivateAnimation(false /* reverse */);
         mActivated = true;
         if (mOnActivatedListener != null) {
@@ -388,19 +317,25 @@
         mBackgroundNormal.animate()
                 .alpha(reverse ? 0f : 1f)
                 .setInterpolator(alphaInterpolator)
-                .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                    @Override
-                    public void onAnimationUpdate(ValueAnimator animation) {
-                        float animatedFraction = animation.getAnimatedFraction();
-                        if (reverse) {
-                            animatedFraction = 1.0f - animatedFraction;
-                        }
-                        setNormalBackgroundVisibilityAmount(animatedFraction);
+                .setUpdateListener(animation -> {
+                    float animatedFraction = animation.getAnimatedFraction();
+                    if (reverse) {
+                        animatedFraction = 1.0f - animatedFraction;
                     }
+                    setNormalBackgroundVisibilityAmount(animatedFraction);
                 })
                 .setDuration(ACTIVATE_ANIMATION_LENGTH);
     }
 
+    @Override
+    public boolean performClick() {
+        if (!mNeedsDimming || (mAccessibilityManager != null
+                && mAccessibilityManager.isTouchExplorationEnabled())) {
+            return super.performClick();
+        }
+        return false;
+    }
+
     /**
      * Cancels the hotspot and makes the notification inactive.
      */
@@ -418,11 +353,13 @@
         if (mOnActivatedListener != null) {
             mOnActivatedListener.onActivationReset(this);
         }
-        removeCallbacks(mTapTimeoutRunnable);
     }
 
     public void setDimmed(boolean dimmed, boolean fade) {
         mNeedsDimming = dimmed;
+        if (mOnDimmedListener != null) {
+            mOnDimmedListener.onSetDimmed(dimmed);
+        }
         dimmed &= isDimmable();
         if (mDimmed != dimmed) {
             mDimmed = dimmed;
@@ -439,13 +376,17 @@
         return true;
     }
 
+    public boolean isDimmed() {
+        return mDimmed;
+    }
+
     private void updateOutlineAlpha() {
         float alpha = NotificationStackScrollLayout.BACKGROUND_ALPHA_DIMMED;
         alpha = (alpha + (1.0f - alpha) * mNormalBackgroundVisibilityAmount);
         setOutlineAlpha(alpha);
     }
 
-    public void setNormalBackgroundVisibilityAmount(float normalBackgroundVisibilityAmount) {
+    private void setNormalBackgroundVisibilityAmount(float normalBackgroundVisibilityAmount) {
         mNormalBackgroundVisibilityAmount = normalBackgroundVisibilityAmount;
         updateOutlineAlpha();
     }
@@ -473,14 +414,14 @@
     /**
      * Sets the tint color of the background
      */
-    public void setTintColor(int color) {
+    protected void setTintColor(int color) {
         setTintColor(color, false);
     }
 
     /**
      * Sets the tint color of the background
      */
-    public void setTintColor(int color, boolean animated) {
+    void setTintColor(int color, boolean animated) {
         if (color != mBgTint) {
             mBgTint = color;
             updateBackgroundTint(animated);
@@ -562,13 +503,10 @@
             mStartTint = mCurrentBackgroundTint;
             mTargetTint = color;
             mBackgroundColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
-            mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    int newColor = NotificationUtils.interpolateColors(mStartTint, mTargetTint,
-                            animation.getAnimatedFraction());
-                    setBackgroundTintColor(newColor);
-                }
+            mBackgroundColorAnimator.addUpdateListener(animation -> {
+                int newColor = NotificationUtils.interpolateColors(mStartTint, mTargetTint,
+                        animation.getAnimatedFraction());
+                setBackgroundTintColor(newColor);
             });
             mBackgroundColorAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
             mBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR);
@@ -643,11 +581,11 @@
     }
 
     protected void updateBackgroundAlpha(float transformationAmount) {
-        mBgAlpha =  isChildInGroup() && mDimmed ? transformationAmount : 1f;
+        float bgAlpha = isChildInGroup() && mDimmed ? transformationAmount : 1f;
         if (mDimmedBackgroundFadeInAmount != -1) {
-            mBgAlpha *= mDimmedBackgroundFadeInAmount;
+            bgAlpha *= mDimmedBackgroundFadeInAmount;
         }
-        mBackgroundDimmed.setAlpha(mBgAlpha);
+        mBackgroundDimmed.setAlpha(bgAlpha);
     }
 
     protected void resetBackgroundAlpha() {
@@ -671,7 +609,6 @@
             mBackgroundDimmed.setVisibility(View.INVISIBLE);
             mBackgroundNormal.setVisibility(View.VISIBLE);
             mBackgroundNormal.setAlpha(1f);
-            removeCallbacks(mTapTimeoutRunnable);
             // make in inactive to avoid it sticking around active
             makeInactive(false /* animate */);
         }
@@ -783,14 +720,11 @@
         mAppearAnimator.setInterpolator(Interpolators.LINEAR);
         mAppearAnimator.setDuration(
                 (long) (duration * Math.abs(mAppearAnimationFraction - targetValue)));
-        mAppearAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                mAppearAnimationFraction = (float) animation.getAnimatedValue();
-                updateAppearAnimationAlpha();
-                updateAppearRect();
-                invalidate();
-            }
+        mAppearAnimator.addUpdateListener(animation -> {
+            mAppearAnimationFraction = (float) animation.getAnimatedValue();
+            updateAppearAnimationAlpha();
+            updateAppearRect();
+            invalidate();
         });
         if (animationListener != null) {
             mAppearAnimator.addListener(animationListener);
@@ -921,7 +855,7 @@
                 getCurrentBackgroundRadiusBottom());
     }
 
-    protected void applyBackgroundRoundness(float topRadius, float bottomRadius) {
+    private void applyBackgroundRoundness(float topRadius, float bottomRadius) {
         mBackgroundDimmed.setRoundness(topRadius, bottomRadius);
         mBackgroundNormal.setRoundness(topRadius, bottomRadius);
     }
@@ -963,7 +897,7 @@
         }
     }
 
-    protected int getRippleColor() {
+    private int getRippleColor() {
         if (mBgTint != 0) {
             return mTintedRippleColor;
         } else {
@@ -1010,10 +944,6 @@
         mOnActivatedListener = onActivatedListener;
     }
 
-    public boolean hasSameBgColor(ActivatableNotificationView otherView) {
-        return calculateBgColor() == otherView.calculateBgColor();
-    }
-
     @Override
     public void setFakeShadowIntensity(float shadowIntensity, float outlineAlpha, int shadowYEnd,
             int outlineTranslation) {
@@ -1071,8 +1001,24 @@
         return mRefocusOnDismiss || isAccessibilityFocused();
     }
 
+    void setTouchHandler(Gefingerpoken touchHandler) {
+        mTouchHandler = touchHandler;
+    }
+
+    void setOnDimmedListener(OnDimmedListener onDimmedListener) {
+        mOnDimmedListener = onDimmedListener;
+    }
+
+    public void setAccessibilityManager(AccessibilityManager accessibilityManager) {
+        mAccessibilityManager = accessibilityManager;
+    }
+
     public interface OnActivatedListener {
         void onActivated(ActivatableNotificationView view);
         void onActivationReset(ActivatableNotificationView view);
     }
+
+    interface OnDimmedListener {
+        void onSetDimmed(boolean dimmed);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
index 18993ff..8465658 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java
@@ -16,9 +16,13 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import android.view.MotionEvent;
+import android.view.View;
 import android.view.accessibility.AccessibilityManager;
 
+import com.android.systemui.Gefingerpoken;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.phone.DoubleTapHelper;
 
 import javax.inject.Inject;
 
@@ -27,21 +31,102 @@
  */
 public class ActivatableNotificationViewController {
     private final ActivatableNotificationView mView;
+    private final ExpandableOutlineViewController mExpandableOutlineViewController;
     private final AccessibilityManager mAccessibilityManager;
     private final FalsingManager mFalsingManager;
+    private DoubleTapHelper mDoubleTapHelper;
+    private boolean mNeedsDimming;
+
+    private TouchHandler mTouchHandler = new TouchHandler();
 
     @Inject
     public ActivatableNotificationViewController(ActivatableNotificationView view,
+            ExpandableOutlineViewController expandableOutlineViewController,
             AccessibilityManager accessibilityManager, FalsingManager falsingManager) {
         mView = view;
+        mExpandableOutlineViewController = expandableOutlineViewController;
         mAccessibilityManager = accessibilityManager;
         mFalsingManager = falsingManager;
+
+        mView.setOnActivatedListener(new ActivatableNotificationView.OnActivatedListener() {
+            @Override
+            public void onActivated(ActivatableNotificationView view) {
+                mFalsingManager.onNotificationActive();
+            }
+
+            @Override
+            public void onActivationReset(ActivatableNotificationView view) {
+            }
+        });
     }
 
     /**
      * Initialize the controller, setting up handlers and other behavior.
      */
     public void init() {
+        mExpandableOutlineViewController.init();
+        mDoubleTapHelper = new DoubleTapHelper(mView, (active) -> {
+            if (active) {
+                mView.makeActive();
+                mFalsingManager.onNotificationActive();
+            } else {
+                mView.makeInactive(true /* animate */);
+            }
+        }, mView::performClick, mView::handleSlideBack, mFalsingManager::onNotificationDoubleTap);
+        mView.setOnTouchListener(mTouchHandler);
+        mView.setTouchHandler(mTouchHandler);
+        mView.setOnDimmedListener(dimmed -> {
+            mNeedsDimming = dimmed;
+        });
+        mView.setAccessibilityManager(mAccessibilityManager);
+    }
 
+    class TouchHandler implements Gefingerpoken, View.OnTouchListener {
+        private boolean mBlockNextTouch;
+
+        @Override
+        public boolean onTouch(View v, MotionEvent ev) {
+            boolean result;
+            if (mBlockNextTouch) {
+                mBlockNextTouch = false;
+                return true;
+            }
+            if (mNeedsDimming && !mAccessibilityManager.isTouchExplorationEnabled()
+                    && mView.isInteractive()) {
+                if (mNeedsDimming && !mView.isDimmed()) {
+                    // We're actually dimmed, but our content isn't dimmable,
+                    // let's ensure we have a ripple
+                    return false;
+                }
+                result = mDoubleTapHelper.onTouchEvent(ev, mView.getActualHeight());
+            } else {
+                return false;
+            }
+            return result;
+        }
+
+        @Override
+        public boolean onInterceptTouchEvent(MotionEvent ev) {
+            if (mNeedsDimming && ev.getActionMasked() == MotionEvent.ACTION_DOWN
+                    && mView.disallowSingleClick(ev)
+                    && !mAccessibilityManager.isTouchExplorationEnabled()) {
+                if (!mView.isActivated()) {
+                    return true;
+                } else if (!mDoubleTapHelper.isWithinDoubleTapSlop(ev)) {
+                    mBlockNextTouch = true;
+                    mView.makeInactive(true /* animate */);
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Use {@link #onTouch(View, MotionEvent) instead}.
+         */
+        @Override
+        public boolean onTouchEvent(MotionEvent ev) {
+            return false;
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index c34bba7..55173182 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -42,7 +42,6 @@
 import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.SystemClock;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
 import android.util.AttributeSet;
@@ -72,11 +71,11 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarIconView;
@@ -199,6 +198,7 @@
     private NotificationGuts mGuts;
     private NotificationEntry mEntry;
     private String mAppName;
+    private FalsingManager mFalsingManager;
 
     /**
      * Whether or not the notification is using the heads up view and should peek from the top.
@@ -1087,20 +1087,6 @@
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mEntry.setInitializationTime(SystemClock.elapsedRealtime());
-        Dependency.get(PluginManager.class).addPluginListener(this,
-                NotificationMenuRowPlugin.class, false /* Allow multiple */);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        Dependency.get(PluginManager.class).removePluginListener(this);
-    }
-
-    @Override
     public void onPluginConnected(NotificationMenuRowPlugin plugin, Context pluginContext) {
         boolean existed = mMenuRow != null && mMenuRow.getMenuView() != null;
         if (existed) {
@@ -1439,7 +1425,7 @@
         return mIsBlockingHelperShowing && mNotificationTranslationFinished;
     }
 
-    public void setOnDismissRunnable(Runnable onDismissRunnable) {
+    void setOnDismissRunnable(Runnable onDismissRunnable) {
         mOnDismissRunnable = onDismissRunnable;
     }
 
@@ -1597,7 +1583,6 @@
         mMenuRow = new NotificationMenuRow(mContext);
         mImageResolver = new NotificationInlineImageResolver(context,
                 new NotificationInlineImageCache());
-        mMediaManager = Dependency.get(NotificationMediaManager.class);
         initDimens();
     }
 
@@ -1612,7 +1597,11 @@
             NotificationGroupManager groupManager,
             HeadsUpManager headsUpManager,
             RowContentBindStage rowContentBindStage,
-            OnExpandClickListener onExpandClickListener) {
+            OnExpandClickListener onExpandClickListener,
+            NotificationMediaManager notificationMediaManager,
+            OnAppOpsClickListener onAppOpsClickListener,
+            FalsingManager falsingManager,
+            StatusBarStateController statusBarStateController) {
         mAppName = appName;
         if (mMenuRow != null && mMenuRow.getMenuView() != null) {
             mMenuRow.setAppName(mAppName);
@@ -1625,9 +1614,9 @@
         mHeadsUpManager = headsUpManager;
         mRowContentBindStage = rowContentBindStage;
         mOnExpandClickListener = onExpandClickListener;
-    }
-
-    public void setStatusBarStateController(StatusBarStateController statusBarStateController) {
+        mMediaManager = notificationMediaManager;
+        setAppOpsOnClickListener(onAppOpsClickListener);
+        mFalsingManager = falsingManager;
         mStatusbarStateController = statusBarStateController;
     }
 
@@ -1719,7 +1708,7 @@
         return mOnAppOpsClickListener;
     }
 
-    public void setAppOpsOnClickListener(ExpandableNotificationRow.OnAppOpsClickListener l) {
+    void setAppOpsOnClickListener(ExpandableNotificationRow.OnAppOpsClickListener l) {
         mOnAppOpsClickListener = v -> {
             createMenu();
             NotificationMenuRowPlugin provider = getProvider();
@@ -2188,7 +2177,7 @@
      * @param allowChildExpansion whether a call to this method allows expanding children
      */
     public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) {
-        getFalsingManager().setNotificationExpanded();
+        mFalsingManager.setNotificationExpanded();
         if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion
                 && !mChildrenContainer.showingAsLowPriority()) {
             final boolean wasExpanded = mGroupManager.isGroupExpanded(mEntry.getSbn());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
new file mode 100644
index 0000000..39fab43
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -0,0 +1,161 @@
+/*
+ * 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 static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
+import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.dagger.AppName;
+import com.android.systemui.statusbar.notification.row.dagger.DismissRunnable;
+import com.android.systemui.statusbar.notification.row.dagger.NotificationKey;
+import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.util.time.SystemClock;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Controller for {@link ExpandableNotificationRow}.
+ */
+@NotificationRowScope
+public class ExpandableNotificationRowController {
+    private final ExpandableNotificationRow mView;
+    private final ActivatableNotificationViewController mActivatableNotificationViewController;
+    private final NotificationMediaManager mMediaManager;
+    private final PluginManager mPluginManager;
+    private final SystemClock mClock;
+    private final String mAppName;
+    private final String mNotificationKey;
+    private final KeyguardBypassController mKeyguardBypassController;
+    private final NotificationGroupManager mNotificationGroupManager;
+    private final RowContentBindStage mRowContentBindStage;
+    private final NotificationLogger mNotificationLogger;
+    private final HeadsUpManager mHeadsUpManager;
+    private final ExpandableNotificationRow.OnExpandClickListener mOnExpandClickListener;
+    private final StatusBarStateController mStatusBarStateController;
+    private final NotificationRowContentBinder.InflationCallback mInflationCallback;
+
+    private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
+            this::logNotificationExpansion;
+    private final ExpandableNotificationRow.OnAppOpsClickListener mOnAppOpsClickListener;
+    private final NotificationGutsManager mNotificationGutsManager;
+    private Runnable mOnDismissRunnable;
+    private final FalsingManager mFalsingManager;
+    private final boolean mAllowLongPress;
+
+    @Inject
+    public ExpandableNotificationRowController(ExpandableNotificationRow view,
+            ActivatableNotificationViewController activatableNotificationViewController,
+            NotificationMediaManager mediaManager, PluginManager pluginManager,
+            SystemClock clock, @AppName String appName, @NotificationKey String notificationKey,
+            KeyguardBypassController keyguardBypassController,
+            NotificationGroupManager notificationGroupManager,
+            RowContentBindStage rowContentBindStage,
+            NotificationLogger notificationLogger, HeadsUpManager headsUpManager,
+            ExpandableNotificationRow.OnExpandClickListener onExpandClickListener,
+            StatusBarStateController statusBarStateController,
+            NotificationRowContentBinder.InflationCallback inflationCallback,
+            NotificationGutsManager notificationGutsManager,
+            @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
+            @DismissRunnable Runnable onDismissRunnable, FalsingManager falsingManager) {
+        mView = view;
+        mActivatableNotificationViewController = activatableNotificationViewController;
+        mMediaManager = mediaManager;
+        mPluginManager = pluginManager;
+        mClock = clock;
+        mAppName = appName;
+        mNotificationKey = notificationKey;
+        mKeyguardBypassController = keyguardBypassController;
+        mNotificationGroupManager = notificationGroupManager;
+        mRowContentBindStage = rowContentBindStage;
+        mNotificationLogger = notificationLogger;
+        mHeadsUpManager = headsUpManager;
+        mOnExpandClickListener = onExpandClickListener;
+        mStatusBarStateController = statusBarStateController;
+        mInflationCallback = inflationCallback;
+        mNotificationGutsManager = notificationGutsManager;
+        mOnDismissRunnable = onDismissRunnable;
+        mOnAppOpsClickListener = mNotificationGutsManager::openGuts;
+        mAllowLongPress = allowLongPress;
+        mFalsingManager = falsingManager;
+    }
+
+    /**
+     * Initialize the controller.
+     */
+    public void init() {
+        mActivatableNotificationViewController.init();
+        mView.initialize(
+                mAppName,
+                mNotificationKey,
+                mExpansionLogger,
+                mKeyguardBypassController,
+                mNotificationGroupManager,
+                mHeadsUpManager,
+                mRowContentBindStage,
+                mOnExpandClickListener,
+                mMediaManager,
+                mOnAppOpsClickListener,
+                mFalsingManager,
+                mStatusBarStateController
+        );
+        mView.setOnDismissRunnable(mOnDismissRunnable);
+        mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+        if (mAllowLongPress) {
+            mView.setLongPressListener(mNotificationGutsManager::openGuts);
+        }
+        if (ENABLE_REMOTE_INPUT) {
+            mView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
+        }
+
+        mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
+            @Override
+            public void onViewAttachedToWindow(View v) {
+                mView.getEntry().setInitializationTime(mClock.elapsedRealtime());
+                mPluginManager.addPluginListener(mView,
+                        NotificationMenuRowPlugin.class, false /* Allow multiple */);
+            }
+
+            @Override
+            public void onViewDetachedFromWindow(View v) {
+                mPluginManager.removePluginListener(mView);
+            }
+        });
+    }
+
+    private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
+        mNotificationLogger.onExpansionChanged(key, userAction, expanded);
+    }
+
+    /** */
+    public void setOnDismissRunnable(Runnable onDismissRunnable) {
+        mOnDismissRunnable = onDismissRunnable;
+        mView.setOnDismissRunnable(onDismissRunnable);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineViewController.java
new file mode 100644
index 0000000..75c9d1e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineViewController.java
@@ -0,0 +1,41 @@
+/*
+ * 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 javax.inject.Inject;
+
+/**
+ * Controller for {@link ExpandableOutlineView}.
+ */
+public class ExpandableOutlineViewController {
+    private final ExpandableOutlineView mView;
+    private final ExpandableViewController mExpandableViewController;
+
+    @Inject
+    public ExpandableOutlineViewController(ExpandableOutlineView view,
+            ExpandableViewController expandableViewController) {
+        mView = view;
+        mExpandableViewController = expandableViewController;
+    }
+
+    /**
+     * Initialize the controller.
+     */
+    public void init() {
+        mExpandableViewController.init();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableViewController.java
new file mode 100644
index 0000000..e14ca8c4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableViewController.java
@@ -0,0 +1,37 @@
+/*
+ * 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 javax.inject.Inject;
+
+/**
+ * Controller for {@link ExpandableView}.
+ */
+public class ExpandableViewController {
+    private final ExpandableView mView;
+
+    @Inject
+    public ExpandableViewController(ExpandableView view) {
+        mView = view;
+    }
+
+    /**
+     * Initialize the controller.
+     */
+    public void init() {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index bb0681c..a0af4ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -23,14 +23,6 @@
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
 
-import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_BUBBLE;
-import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_DEMOTE;
-import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_FAVORITE;
-import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_HOME;
-import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_MUTE;
-import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_SNOOZE;
-import static com.android.systemui.statusbar.notification.row.NotificationConversationInfo.UpdateChannelRunnable.ACTION_UNBUBBLE;
-
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.IntDef;
@@ -69,11 +61,13 @@
 import android.widget.TextView;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.phone.ShadeController;
 
 import java.lang.annotation.Retention;
 import java.util.Arrays;
@@ -92,6 +86,7 @@
     ShortcutManager mShortcutManager;
     private PackageManager mPm;
     private VisualStabilityManager mVisualStabilityManager;
+    private ShadeController mShadeController;
 
     private String mPackageName;
     private String mAppName;
@@ -103,24 +98,30 @@
     private NotificationEntry mEntry;
     private StatusBarNotification mSbn;
     private boolean mIsDeviceProvisioned;
-    private int mStartingChannelImportance;
     private boolean mStartedAsBubble;
     private boolean mIsBubbleable;
-    // TODO: remove when launcher api works
-    @VisibleForTesting
-    boolean mShowHomeScreen = false;
 
-    private @UpdateChannelRunnable.Action int mSelectedAction = -1;
+    private @Action int mSelectedAction = -1;
 
     private OnSnoozeClickListener mOnSnoozeClickListener;
     private OnSettingsClickListener mOnSettingsClickListener;
-    private OnAppSettingsClickListener mAppSettingsClickListener;
     private NotificationGuts mGutsContainer;
     private BubbleController mBubbleController;
 
     @VisibleForTesting
     boolean mSkipPost = false;
 
+    @Retention(SOURCE)
+    @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE})
+    private @interface Action {}
+    static final int ACTION_BUBBLE = 0;
+    static final int ACTION_HOME = 1;
+    static final int ACTION_FAVORITE = 2;
+    static final int ACTION_SNOOZE = 3;
+    static final int ACTION_MUTE = 4;
+    static final int ACTION_SETTINGS = 5;
+    static final int ACTION_UNBUBBLE = 6;
+
     private OnClickListener mOnBubbleClick = v -> {
         mSelectedAction = mStartedAsBubble ? ACTION_UNBUBBLE : ACTION_BUBBLE;
         if (mStartedAsBubble) {
@@ -136,12 +137,14 @@
     private OnClickListener mOnHomeClick = v -> {
         mSelectedAction = ACTION_HOME;
         mShortcutManager.requestPinShortcut(mShortcutInfo, null);
+        mShadeController.animateCollapsePanels();
         closeControls(v, true);
     };
 
     private OnClickListener mOnFavoriteClick = v -> {
         mSelectedAction = ACTION_FAVORITE;
-        closeControls(v, true);
+        updateChannel();
+
     };
 
     private OnClickListener mOnSnoozeClick = v -> {
@@ -151,13 +154,8 @@
     };
 
     private OnClickListener mOnMuteClick = v -> {
-      mSelectedAction = ACTION_MUTE;
-      closeControls(v, true);
-    };
-
-    private OnClickListener mOnDemoteClick = v -> {
-        mSelectedAction = ACTION_DEMOTE;
-        closeControls(v, true);
+        mSelectedAction = ACTION_MUTE;
+        updateChannel();
     };
 
     public NotificationConversationInfo(Context context, AttributeSet attrs) {
@@ -197,15 +195,14 @@
         mEntry = entry;
         mSbn = entry.getSbn();
         mPm = pm;
-        mAppSettingsClickListener = onAppSettingsClick;
         mAppName = mPackageName;
         mOnSettingsClickListener = onSettingsClick;
         mNotificationChannel = notificationChannel;
-        mStartingChannelImportance = mNotificationChannel.getImportance();
         mAppUid = mSbn.getUid();
         mDelegatePkg = mSbn.getOpPkg();
         mIsDeviceProvisioned = isDeviceProvisioned;
         mOnSnoozeClickListener = onSnoozeClickListener;
+        mShadeController = Dependency.get(ShadeController.class);
 
         mShortcutManager = shortcutManager;
         mLauncherApps = launcherApps;
@@ -251,9 +248,6 @@
                 mNotificationChannel = mINotificationManager.getConversationNotificationChannel(
                         mContext.getOpPackageName(), UserHandle.getUserId(mAppUid), mPackageName,
                         mNotificationChannel.getId(), false, mConversationId);
-
-                // TODO: ask LA to pin the shortcut once api exists for pinning one shortcut at a
-                // time
             } catch (RemoteException e) {
                 Slog.e(TAG, "Could not create conversation channel", e);
             }
@@ -274,40 +268,24 @@
 
         Button home = findViewById(R.id.home);
         home.setOnClickListener(mOnHomeClick);
-        home.setVisibility(mShowHomeScreen && mShortcutInfo != null
+        home.setVisibility(mShortcutInfo != null
                 && mShortcutManager.isRequestPinShortcutSupported()
                 ? VISIBLE : GONE);
 
-        Button favorite = findViewById(R.id.fave);
+        View favorite = findViewById(R.id.fave);
         favorite.setOnClickListener(mOnFavoriteClick);
-        if (mNotificationChannel.isImportantConversation()) {
-            favorite.setText(R.string.notification_conversation_unfavorite);
-            favorite.setCompoundDrawablesRelative(
-                    mContext.getDrawable(R.drawable.ic_star), null, null, null);
-        } else {
-            favorite.setText(R.string.notification_conversation_favorite);
-            favorite.setCompoundDrawablesRelative(
-                    mContext.getDrawable(R.drawable.ic_star_border), null, null, null);
-        }
 
         Button snooze = findViewById(R.id.snooze);
         snooze.setOnClickListener(mOnSnoozeClick);
 
-        Button mute = findViewById(R.id.mute);
+        View mute = findViewById(R.id.mute);
         mute.setOnClickListener(mOnMuteClick);
-        if (mStartingChannelImportance >= IMPORTANCE_DEFAULT
-                || mStartingChannelImportance == IMPORTANCE_UNSPECIFIED) {
-            mute.setText(R.string.notification_conversation_mute);
-            favorite.setCompoundDrawablesRelative(
-                    mContext.getDrawable(R.drawable.ic_notifications_silence), null, null, null);
-        } else {
-            mute.setText(R.string.notification_conversation_unmute);
-            favorite.setCompoundDrawablesRelative(
-                    mContext.getDrawable(R.drawable.ic_notifications_alert), null, null, null);
-        }
 
-        ImageButton demote = findViewById(R.id.demote);
-        demote.setOnClickListener(mOnDemoteClick);
+        final View settingsButton = findViewById(R.id.info);
+        settingsButton.setOnClickListener(getSettingsOnClickListener());
+        settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE);
+
+        updateToggleActions();
     }
 
     private void bindHeader() {
@@ -315,26 +293,6 @@
 
         // Delegate
         bindDelegate();
-
-        // Set up app settings link (i.e. Customize)
-        View settingsLinkView = findViewById(R.id.app_settings);
-        Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName,
-                mNotificationChannel,
-                mSbn.getId(), mSbn.getTag());
-        if (settingsIntent != null
-                && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
-            settingsLinkView.setVisibility(VISIBLE);
-            settingsLinkView.setOnClickListener((View view) -> {
-                mAppSettingsClickListener.onClick(view, settingsIntent);
-            });
-        } else {
-            settingsLinkView.setVisibility(View.GONE);
-        }
-
-        // System Settings button.
-        final View settingsButton = findViewById(R.id.info);
-        settingsButton.setOnClickListener(getSettingsOnClickListener());
-        settingsButton.setVisibility(settingsButton.hasOnClickListeners() ? VISIBLE : GONE);
     }
 
     private OnClickListener getSettingsOnClickListener() {
@@ -424,15 +382,12 @@
 
     private void bindDelegate() {
         TextView delegateView = findViewById(R.id.delegate_name);
-        TextView dividerView = findViewById(R.id.pkg_divider);
 
         if (!TextUtils.equals(mPackageName, mDelegatePkg)) {
             // this notification was posted by a delegate!
             delegateView.setVisibility(View.VISIBLE);
-            dividerView.setVisibility(View.VISIBLE);
         } else {
             delegateView.setVisibility(View.GONE);
-            dividerView.setVisibility(View.GONE);
         }
     }
 
@@ -492,26 +447,37 @@
         }
     }
 
-    private Intent getAppSettingsIntent(PackageManager pm, String packageName,
-            NotificationChannel channel, int id, String tag) {
-        Intent intent = new Intent(Intent.ACTION_MAIN)
-                .addCategory(Notification.INTENT_CATEGORY_NOTIFICATION_PREFERENCES)
-                .setPackage(packageName);
-        final List<ResolveInfo> resolveInfos = pm.queryIntentActivities(
-                intent,
-                PackageManager.MATCH_DEFAULT_ONLY
-        );
-        if (resolveInfos == null || resolveInfos.size() == 0 || resolveInfos.get(0) == null) {
-            return null;
+    private void updateToggleActions() {
+        ImageButton favorite = findViewById(R.id.fave);
+        if (mNotificationChannel.isImportantConversation()) {
+            favorite.setContentDescription(
+                    mContext.getString(R.string.notification_conversation_favorite));
+            favorite.setImageResource(R.drawable.ic_important);
+        } else {
+            favorite.setContentDescription(
+                    mContext.getString(R.string.notification_conversation_unfavorite));
+            favorite.setImageResource(R.drawable.ic_important_outline);
         }
-        final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
-        intent.setClassName(activityInfo.packageName, activityInfo.name);
-        if (channel != null) {
-            intent.putExtra(Notification.EXTRA_CHANNEL_ID, channel.getId());
+
+        ImageButton mute = findViewById(R.id.mute);
+        if (mNotificationChannel.getImportance() >= IMPORTANCE_DEFAULT
+                || mNotificationChannel.getImportance() == IMPORTANCE_UNSPECIFIED) {
+            mute.setContentDescription(
+                    mContext.getString(R.string.notification_conversation_unmute));
+            mute.setImageResource(R.drawable.ic_notifications_alert);
+        } else {
+            mute.setContentDescription(
+                    mContext.getString(R.string.notification_conversation_mute));
+            mute.setImageResource(R.drawable.ic_notifications_silence);
         }
-        intent.putExtra(Notification.EXTRA_NOTIFICATION_ID, id);
-        intent.putExtra(Notification.EXTRA_NOTIFICATION_TAG, tag);
-        return intent;
+    }
+
+    private void updateChannel() {
+        Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
+        bgHandler.post(
+                new UpdateChannelRunnable(mINotificationManager, mPackageName,
+                        mAppUid, mSelectedAction, mNotificationChannel));
+        mVisualStabilityManager.temporarilyAllowReordering();
     }
 
     /**
@@ -556,11 +522,7 @@
     @Override
     public boolean handleCloseControls(boolean save, boolean force) {
         if (save && mSelectedAction > -1) {
-            Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
-            bgHandler.post(
-                    new UpdateChannelRunnable(mINotificationManager, mPackageName,
-                            mAppUid, mSelectedAction, mNotificationChannel));
-            mVisualStabilityManager.temporarilyAllowReordering();
+            updateChannel();
         }
         return false;
     }
@@ -575,19 +537,7 @@
         return false;
     }
 
-    static class UpdateChannelRunnable implements Runnable {
-
-        @Retention(SOURCE)
-        @IntDef({ACTION_BUBBLE, ACTION_HOME, ACTION_FAVORITE, ACTION_SNOOZE, ACTION_MUTE,
-                ACTION_DEMOTE})
-        private @interface Action {}
-        static final int ACTION_BUBBLE = 0;
-        static final int ACTION_HOME = 1;
-        static final int ACTION_FAVORITE = 2;
-        static final int ACTION_SNOOZE = 3;
-        static final int ACTION_MUTE = 4;
-        static final int ACTION_DEMOTE = 5;
-        static final int ACTION_UNBUBBLE = 6;
+    class UpdateChannelRunnable implements Runnable {
 
         private final INotificationManager mINotificationManager;
         private final String mAppPkg;
@@ -633,10 +583,6 @@
                                     mChannelToUpdate.getOriginalImportance(), IMPORTANCE_DEFAULT));
                         }
                         break;
-                    case ACTION_DEMOTE:
-                        mChannelToUpdate.setDemoted(!mChannelToUpdate.isDemoted());
-                        break;
-
                 }
 
                 if (channelSettingChanged) {
@@ -646,13 +592,7 @@
             } catch (RemoteException e) {
                 Log.e(TAG, "Unable to update notification channel", e);
             }
+            ThreadUtils.postOnMainThread(() -> updateToggleActions());
         }
     }
-
-    @Retention(SOURCE)
-    @IntDef({BEHAVIOR_ALERTING, BEHAVIOR_SILENT, BEHAVIOR_BUBBLE})
-    private @interface AlertingBehavior {}
-    private static final int BEHAVIOR_ALERTING = 0;
-    private static final int BEHAVIOR_SILENT = 1;
-    private static final int BEHAVIOR_BUBBLE = 2;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 6789c81..352abcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -58,6 +58,7 @@
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
 import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
@@ -384,6 +385,7 @@
                 guts.resetFalsingCheck();
                 mOnSettingsClickListener.onSettingsClick(sbn.getKey());
                 startAppNotificationSettingsActivity(packageName, appUid, channel, row);
+                notificationInfoView.closeControls(v, false);
             };
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
index c173b4d..6feffe6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java
@@ -26,7 +26,6 @@
 import com.android.systemui.R;
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
 
 import javax.inject.Inject;
 
@@ -37,7 +36,6 @@
 
     private static final String TAG = "RowInflaterTask";
     private static final boolean TRACE_ORIGIN = true;
-    private final NotificationRowComponent.Builder mNotificationRowComponentBuilder;
 
     private RowInflationFinishedListener mListener;
     private NotificationEntry mEntry;
@@ -45,10 +43,7 @@
     private Throwable mInflateOrigin;
 
     @Inject
-    public RowInflaterTask(
-            NotificationRowComponent.Builder notificationRowComponentBuilder) {
-        super();
-        mNotificationRowComponentBuilder = notificationRowComponentBuilder;
+    public RowInflaterTask() {
     }
 
     /**
@@ -75,12 +70,6 @@
     public void onInflateFinished(View view, int resid, ViewGroup parent) {
         if (!mCancelled) {
             try {
-                // Setup the controller for the view.
-                NotificationRowComponent component = mNotificationRowComponentBuilder
-                        .activatableNotificationView((ActivatableNotificationView) view)
-                        .build();
-                component.getActivatableNotificationViewController().init();
-
                 mEntry.onInflationTaskFinished();
                 mListener.onInflationFinished((ExpandableNotificationRow) view);
             } catch (Throwable t) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ActivatableNotificationViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ActivatableNotificationViewModule.java
new file mode 100644
index 0000000..a3dfa60
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ActivatableNotificationViewModule.java
@@ -0,0 +1,37 @@
+/*
+ * 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.dagger;
+
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableOutlineView;
+import com.android.systemui.statusbar.notification.row.ExpandableView;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Module for NotificationRowComponent.
+ */
+@Module
+public interface ActivatableNotificationViewModule {
+    /** ExpandableView is provided as an instance of ActivatableNotificationView. */
+    @Binds
+    ExpandableView bindExpandableView(ActivatableNotificationView view);
+    /** ExpandableOutlineView is provided as an instance of ActivatableNotificationView. */
+    @Binds
+    ExpandableOutlineView bindExpandableOutlineView(ActivatableNotificationView view);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/AppName.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/AppName.java
new file mode 100644
index 0000000..1dbca0c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/AppName.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface AppName {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/DismissRunnable.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/DismissRunnable.java
new file mode 100644
index 0000000..4331142
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/DismissRunnable.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface DismissRunnable {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
new file mode 100644
index 0000000..6d6d3e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
@@ -0,0 +1,122 @@
+/*
+ * 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.dagger;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
+import com.android.systemui.statusbar.notification.row.RowContentBindStage;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import dagger.Binds;
+import dagger.BindsInstance;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * Dagger Component for a {@link ExpandableNotificationRow}.
+ */
+@Subcomponent(modules = {ExpandableNotificationRowComponent.ExpandableNotificationRowModule.class,
+        ActivatableNotificationViewModule.class})
+@NotificationRowScope
+public interface ExpandableNotificationRowComponent {
+
+    /**
+     * Builder for {@link NotificationRowComponent}.
+     */
+    @Subcomponent.Builder
+    interface Builder {
+        // TODO: NotificationEntry contains a reference to ExpandableNotificationRow, so it
+        // should be possible to pull one from the other, but they aren't connected at the time
+        // this component is constructed.
+        @BindsInstance
+        Builder expandableNotificationRow(ExpandableNotificationRow view);
+        @BindsInstance
+        Builder notificationEntry(NotificationEntry entry);
+        @BindsInstance
+        Builder onDismissRunnable(@DismissRunnable Runnable runnable);
+        @BindsInstance
+        Builder rowContentBindStage(RowContentBindStage rowContentBindStage);
+        @BindsInstance
+        Builder inflationCallback(NotificationRowContentBinder.InflationCallback inflationCallback);
+        @BindsInstance
+        Builder onExpandClickListener(ExpandableNotificationRow.OnExpandClickListener presenter);
+        ExpandableNotificationRowComponent build();
+    }
+
+    /**
+     * Creates a ExpandableNotificationRowController.
+     */
+    @NotificationRowScope
+    ExpandableNotificationRowController getExpandableNotificationRowController();
+
+    /**
+     * Dagger Module that extracts interesting properties from an ExpandableNotificationRow.
+     */
+    @Module
+    abstract class ExpandableNotificationRowModule {
+
+        /** ExpandableNotificationRow is provided as an instance of ActivatableNotificationView. */
+        @Binds
+        abstract ActivatableNotificationView bindExpandableView(ExpandableNotificationRow view);
+
+        @Provides
+        static StatusBarNotification provideStatusBarNotification(
+                NotificationEntry notificationEntry) {
+            return notificationEntry.getSbn();
+        }
+
+        @Provides
+        @NotificationKey
+        static String provideNotificationKey(StatusBarNotification statusBarNotification) {
+            return statusBarNotification.getKey();
+        }
+
+        @Provides
+        @AppName
+        static String provideAppName(Context context, StatusBarNotification statusBarNotification) {
+            // Get the app name.
+            // Note that Notification.Builder#bindHeaderAppName has similar logic
+            // but since this field is used in the guts, it must be accurate.
+            // Therefore we will only show the application label, or, failing that, the
+            // package name. No substitutions.
+            PackageManager pmUser = StatusBar.getPackageManagerForUser(
+                    context, statusBarNotification.getUser().getIdentifier());
+            final String pkg = statusBarNotification.getPackageName();
+            try {
+                final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES
+                                | PackageManager.MATCH_DISABLED_COMPONENTS);
+                if (info != null) {
+                    return String.valueOf(pmUser.getApplicationLabel(info));
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                // Do nothing
+            }
+
+            return pkg;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationKey.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationKey.java
new file mode 100644
index 0000000..b1fff38
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationKey.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface NotificationKey {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowComponent.java
index f16ea7a..1f535c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowComponent.java
@@ -16,24 +16,17 @@
 
 package com.android.systemui.statusbar.notification.row.dagger;
 
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-
-import javax.inject.Scope;
-
 import dagger.BindsInstance;
 import dagger.Subcomponent;
 
 /**
  * Dagger subcomponent for Notification related views.
  */
-@Subcomponent(modules = {})
-@NotificationRowComponent.NotificationRowScope
+@Subcomponent(modules = {ActivatableNotificationViewModule.class})
+@NotificationRowScope
 public interface NotificationRowComponent {
     /**
      * Builder for {@link NotificationRowComponent}.
@@ -46,14 +39,6 @@
     }
 
     /**
-     * Scope annotation for singleton items within the StatusBarComponent.
-     */
-    @Documented
-    @Retention(RUNTIME)
-    @Scope
-    @interface NotificationRowScope {}
-
-    /**
      * Creates a ActivatableNotificationViewController.
      */
     @NotificationRowScope
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowScope.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowScope.java
new file mode 100644
index 0000000..4555b83
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowScope.java
@@ -0,0 +1,32 @@
+/*
+ * 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.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+/**
+ * Scope annotation for singleton items within the StatusBarComponent.
+ */
+@Documented
+@Retention(RUNTIME)
+@Scope
+public @interface NotificationRowScope {}
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 72dfa18..d3e44ea 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(),
-                                getContext().getFeatureId(), intent,
+                                intent,
                                 intent.resolveTypeIfNeeded(getContext().getContentResolver()),
                                 null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, o.toBundle(),
                                 UserHandle.CURRENT.getIdentifier());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 9e64748..3f5215e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -269,6 +269,17 @@
         }
     };
 
+    private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
+            new DeviceConfig.OnPropertiesChangedListener() {
+        @Override
+        public void onPropertiesChanged(DeviceConfig.Properties properties) {
+            if (properties.getKeyset().contains(NAV_BAR_HANDLE_FORCE_OPAQUE)) {
+                mForceNavBarHandleOpaque = properties.getBoolean(
+                        NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
+            }
+        }
+    };
+
     @Inject
     public NavigationBarFragment(AccessibilityManagerWrapper accessibilityManagerWrapper,
             DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger,
@@ -298,21 +309,6 @@
         mDivider = divider;
         mRecentsOptional = recentsOptional;
         mHandler = mainHandler;
-
-        mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                NAV_BAR_HANDLE_FORCE_OPAQUE,
-                /* defaultValue = */ true);
-        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post,
-                new DeviceConfig.OnPropertiesChangedListener() {
-                    @Override
-                    public void onPropertiesChanged(DeviceConfig.Properties properties) {
-                        if (properties.getKeyset().contains(NAV_BAR_HANDLE_FORCE_OPAQUE)) {
-                            mForceNavBarHandleOpaque = properties.getBoolean(
-                                    NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
-                        }
-                    }
-                });
     }
 
     // ----- Fragment Lifecycle Callbacks -----
@@ -338,6 +334,13 @@
 
         // Respect the latest disabled-flags.
         mCommandQueue.recomputeDisableFlags(mDisplayId, false);
+
+        mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                NAV_BAR_HANDLE_FORCE_OPAQUE,
+                /* defaultValue = */ true);
+        DeviceConfig.addOnPropertiesChangedListener(
+                DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
     }
 
     @Override
@@ -346,6 +349,8 @@
         mNavigationModeController.removeListener(this);
         mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
         mContentResolver.unregisterContentObserver(mAssistContentObserver);
+
+        DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
     }
 
     @Override
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 4f01cc1..c68d994 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(), mContext.getFeatureId(),
+                        null, mContext.getBasePackageName(),
                         intent,
                         intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                         null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index e3bcdc8..751217f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -39,6 +39,7 @@
 import com.android.systemui.util.time.FakeSystemClock
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNotEquals
 import org.junit.Assert.assertTrue
 import org.junit.Before
 import org.junit.Test
@@ -225,7 +226,7 @@
         val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2)
         val control = builderFromInfo(newControlInfo).build()
 
-        controller.loadForComponent(TEST_COMPONENT) {}
+        controller.loadForComponent(TEST_COMPONENT) { _, _ -> Unit }
 
         reset(persistenceWrapper)
         verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
@@ -293,11 +294,13 @@
         var loaded = false
         val control = builderFromInfo(TEST_CONTROL_INFO).build()
 
-        controller.loadForComponent(TEST_COMPONENT) {
+        controller.loadForComponent(TEST_COMPONENT) { controls, favorites ->
             loaded = true
-            assertEquals(1, it.size)
-            val controlStatus = it[0]
+            assertEquals(1, controls.size)
+            val controlStatus = controls[0]
             assertEquals(ControlStatus(control, false), controlStatus)
+
+            assertTrue(favorites.isEmpty())
         }
 
         verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
@@ -315,14 +318,17 @@
         val control2 = builderFromInfo(TEST_CONTROL_INFO_2).build()
         controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
 
-        controller.loadForComponent(TEST_COMPONENT) {
+        controller.loadForComponent(TEST_COMPONENT) { controls, favorites ->
             loaded = true
-            assertEquals(2, it.size)
-            val controlStatus = it.first { it.control.controlId == TEST_CONTROL_ID }
+            assertEquals(2, controls.size)
+            val controlStatus = controls.first { it.control.controlId == TEST_CONTROL_ID }
             assertEquals(ControlStatus(control, true), controlStatus)
 
-            val controlStatus2 = it.first { it.control.controlId == TEST_CONTROL_ID_2 }
+            val controlStatus2 = controls.first { it.control.controlId == TEST_CONTROL_ID_2 }
             assertEquals(ControlStatus(control2, false), controlStatus2)
+
+            assertEquals(1, favorites.size)
+            assertEquals(TEST_CONTROL_ID, favorites[0])
         }
 
         verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
@@ -338,13 +344,16 @@
         var loaded = false
         controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
 
-        controller.loadForComponent(TEST_COMPONENT) {
+        controller.loadForComponent(TEST_COMPONENT) { controls, favorites ->
             loaded = true
-            assertEquals(1, it.size)
-            val controlStatus = it[0]
+            assertEquals(1, controls.size)
+            val controlStatus = controls[0]
             assertEquals(TEST_CONTROL_ID, controlStatus.control.controlId)
             assertTrue(controlStatus.favorite)
             assertTrue(controlStatus.removed)
+
+            assertEquals(1, favorites.size)
+            assertEquals(TEST_CONTROL_ID, favorites[0])
         }
 
         verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
@@ -361,7 +370,7 @@
         val newControlInfo = TEST_CONTROL_INFO.copy(controlTitle = TEST_CONTROL_TITLE_2)
         val control = builderFromInfo(newControlInfo).build()
 
-        controller.loadForComponent(TEST_COMPONENT) {}
+        controller.loadForComponent(TEST_COMPONENT) { _, _ -> Unit }
 
         verify(bindingController).bindAndLoad(eq(TEST_COMPONENT),
                 capture(controlLoadCallbackCaptor))
@@ -483,4 +492,81 @@
         assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT))
         assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT_2))
     }
+
+    @Test
+    fun testGetFavoritesForComponent() {
+        controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
+        assertEquals(listOf(TEST_CONTROL_INFO), controller.getFavoritesForComponent(TEST_COMPONENT))
+    }
+
+    @Test
+    fun testGetFavoritesForComponent_otherComponent() {
+        controller.changeFavoriteStatus(TEST_CONTROL_INFO_2, true)
+        assertTrue(controller.getFavoritesForComponent(TEST_COMPONENT).isEmpty())
+    }
+
+    @Test
+    fun testGetFavoritesForComponent_multipleInOrder() {
+        val controlInfo = ControlInfo(TEST_COMPONENT, "id", "title", 0)
+
+        controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
+        controller.changeFavoriteStatus(controlInfo, true)
+
+        assertEquals(listOf(TEST_CONTROL_INFO, controlInfo),
+            controller.getFavoritesForComponent(TEST_COMPONENT))
+
+        controller.clearFavorites()
+
+        controller.changeFavoriteStatus(controlInfo, true)
+        controller.changeFavoriteStatus(TEST_CONTROL_INFO, true)
+
+        assertEquals(listOf(controlInfo, TEST_CONTROL_INFO),
+            controller.getFavoritesForComponent(TEST_COMPONENT))
+    }
+
+    @Test
+    fun testReplaceFavoritesForComponent_noFavorites() {
+        controller.replaceFavoritesForComponent(TEST_COMPONENT, listOf(TEST_CONTROL_INFO))
+
+        assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT))
+        assertEquals(listOf(TEST_CONTROL_INFO), controller.getFavoritesForComponent(TEST_COMPONENT))
+    }
+
+    @Test
+    fun testReplaceFavoritesForComponent_differentComponentsAreFilteredOut() {
+        controller.replaceFavoritesForComponent(TEST_COMPONENT,
+            listOf(TEST_CONTROL_INFO, TEST_CONTROL_INFO_2))
+
+        assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT))
+        assertEquals(listOf(TEST_CONTROL_INFO), controller.getFavoritesForComponent(TEST_COMPONENT))
+    }
+
+    @Test
+    fun testReplaceFavoritesForComponent_oldFavoritesRemoved() {
+        val controlInfo = ControlInfo(TEST_COMPONENT, "id", "title", 0)
+        assertNotEquals(TEST_CONTROL_INFO, controlInfo)
+
+        controller.changeFavoriteStatus(controlInfo, true)
+        controller.replaceFavoritesForComponent(TEST_COMPONENT, listOf(TEST_CONTROL_INFO))
+
+        assertEquals(1, controller.countFavoritesForComponent(TEST_COMPONENT))
+        assertEquals(listOf(TEST_CONTROL_INFO), controller.getFavoritesForComponent(TEST_COMPONENT))
+    }
+
+    @Test
+    fun testReplaceFavoritesForComponent_favoritesInOrder() {
+        val controlInfo = ControlInfo(TEST_COMPONENT, "id", "title", 0)
+
+        val listOrder1 = listOf(TEST_CONTROL_INFO, controlInfo)
+        controller.replaceFavoritesForComponent(TEST_COMPONENT, listOrder1)
+
+        assertEquals(2, controller.countFavoritesForComponent(TEST_COMPONENT))
+        assertEquals(listOrder1, controller.getFavoritesForComponent(TEST_COMPONENT))
+
+        val listOrder2 = listOf(controlInfo, TEST_CONTROL_INFO)
+        controller.replaceFavoritesForComponent(TEST_COMPONENT, listOrder2)
+
+        assertEquals(2, controller.countFavoritesForComponent(TEST_COMPONENT))
+        assertEquals(listOrder2, controller.getFavoritesForComponent(TEST_COMPONENT))
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoriteModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoriteModelTest.kt
new file mode 100644
index 0000000..9ffc29e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoriteModelTest.kt
@@ -0,0 +1,198 @@
+/*
+ * 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.management
+
+import android.app.PendingIntent
+import android.service.controls.Control
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlStatus
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+open class FavoriteModelTest : SysuiTestCase() {
+
+    @Mock
+    lateinit var pendingIntent: PendingIntent
+    @Mock
+    lateinit var allAdapter: ControlAdapter
+    @Mock
+    lateinit var favoritesAdapter: ControlAdapter
+
+    val idPrefix = "controlId"
+    val favoritesIndices = listOf(7, 3, 1, 9)
+    val favoritesList = favoritesIndices.map { "controlId$it" }
+    lateinit var controls: List<ControlStatus>
+
+    lateinit var model: FavoriteModel
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        // controlId0 --> zone = 0
+        // controlId1 --> zone = 1, favorite
+        // controlId2 --> zone = 2
+        // controlId3 --> zone = 0, favorite
+        // controlId4 --> zone = 1
+        // controlId5 --> zone = 2
+        // controlId6 --> zone = 0
+        // controlId7 --> zone = 1, favorite
+        // controlId8 --> zone = 2
+        // controlId9 --> zone = 0, favorite
+        controls = (0..9).map {
+            ControlStatus(
+                    Control.StatelessBuilder("$idPrefix$it", pendingIntent)
+                            .setZone((it % 3).toString())
+                            .build(),
+                    it in favoritesIndices
+            )
+        }
+
+        model = FavoriteModel(controls, favoritesList, favoritesAdapter, allAdapter)
+    }
+}
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class FavoriteModelNonParametrizedTests : FavoriteModelTest() {
+    @Test
+    fun testAll() {
+        // Zones are sorted alphabetically
+        val expected = listOf(
+                ZoneNameWrapper("0"),
+                ControlWrapper(controls[0]),
+                ControlWrapper(controls[3]),
+                ControlWrapper(controls[6]),
+                ControlWrapper(controls[9]),
+                ZoneNameWrapper("1"),
+                ControlWrapper(controls[1]),
+                ControlWrapper(controls[4]),
+                ControlWrapper(controls[7]),
+                ZoneNameWrapper("2"),
+                ControlWrapper(controls[2]),
+                ControlWrapper(controls[5]),
+                ControlWrapper(controls[8])
+        )
+        assertEquals(expected, model.all)
+    }
+
+    @Test
+    fun testFavoritesInOrder() {
+        val expected = favoritesIndices.map { ControlWrapper(controls[it]) }
+        assertEquals(expected, model.favorites)
+    }
+
+    @Test
+    fun testChangeFavoriteStatus_addFavorite() {
+        val controlToAdd = 6
+        model.changeFavoriteStatus("$idPrefix$controlToAdd", true)
+
+        val pair = model.all.findControl(controlToAdd)
+        pair?.let {
+            assertTrue(it.second.favorite)
+            assertEquals(it.second, model.favorites.last().controlStatus)
+            verify(favoritesAdapter).notifyItemInserted(model.favorites.size - 1)
+            verify(allAdapter).notifyItemChanged(it.first)
+            verifyNoMoreInteractions(favoritesAdapter, allAdapter)
+        } ?: run {
+            fail("control not found")
+        }
+    }
+
+    @Test
+    fun testChangeFavoriteStatus_removeFavorite() {
+        val controlToRemove = 3
+        model.changeFavoriteStatus("$idPrefix$controlToRemove", false)
+
+        val pair = model.all.findControl(controlToRemove)
+        pair?.let {
+            assertFalse(it.second.favorite)
+            assertTrue(model.favorites.none {
+                it.controlStatus.control.controlId == "$idPrefix$controlToRemove"
+            })
+            verify(favoritesAdapter).notifyItemRemoved(favoritesIndices.indexOf(controlToRemove))
+            verify(allAdapter).notifyItemChanged(it.first)
+            verifyNoMoreInteractions(favoritesAdapter, allAdapter)
+        } ?: run {
+            fail("control not found")
+        }
+    }
+
+    @Test
+    fun testChangeFavoriteStatus_sameStatus() {
+        model.changeFavoriteStatus("${idPrefix}7", true)
+        model.changeFavoriteStatus("${idPrefix}6", false)
+
+        val expected = favoritesIndices.map { ControlWrapper(controls[it]) }
+        assertEquals(expected, model.favorites)
+
+        verifyNoMoreInteractions(favoritesAdapter, allAdapter)
+    }
+
+    private fun List<ElementWrapper>.findControl(controlIndex: Int): Pair<Int, ControlStatus>? {
+        val index = indexOfFirst {
+            it is ControlWrapper &&
+                it.controlStatus.control.controlId == "$idPrefix$controlIndex"
+        }
+        return if (index == -1) null else index to (get(index) as ControlWrapper).controlStatus
+    }
+}
+
+@SmallTest
+@RunWith(Parameterized::class)
+class FavoriteModelParameterizedTest(val from: Int, val to: Int) : FavoriteModelTest() {
+
+    companion object {
+        @JvmStatic
+        @Parameterized.Parameters(name = "{0} -> {1}")
+        fun data(): Collection<Array<Int>> {
+            return (0..3).flatMap { from ->
+                (0..3).map { to ->
+                    arrayOf(from, to)
+                }
+            }.filterNot { it[0] == it[1] }
+        }
+    }
+
+    @Test
+    fun testMoveItem() {
+        val originalFavorites = model.favorites.toList()
+        val originalFavoritesIds =
+                model.favorites.map { it.controlStatus.control.controlId }.toSet()
+        model.onMoveItem(from, to)
+        assertEquals(originalFavorites[from], model.favorites[to])
+        // Check that we still have the same favorites
+        assertEquals(originalFavoritesIds,
+                model.favorites.map { it.controlStatus.control.controlId }.toSet())
+
+        verify(favoritesAdapter).notifyItemMoved(from, to)
+
+        verifyNoMoreInteractions(allAdapter, favoritesAdapter)
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
index a5722e2..4b47093 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
@@ -16,52 +16,116 @@
 
 package com.android.systemui.glwallpaper;
 
-import static org.junit.Assert.*;
-import static org.mockito.Mockito.RETURNS_DEFAULTS;
-import static org.mockito.Mockito.mock;
+import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atMost;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.graphics.PixelFormat;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
+import android.view.Surface;
+import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
+import android.view.SurfaceSession;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
 
+import org.junit.After;
 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 org.mockito.Spy;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
 public class EglHelperTest extends SysuiTestCase {
 
-    @Mock
+    @Spy
     private EglHelper mEglHelper;
+
     @Mock
     private SurfaceHolder mSurfaceHolder;
 
     @Before
     public void setUp() throws Exception {
-        mEglHelper = mock(EglHelper.class, RETURNS_DEFAULTS);
-        mSurfaceHolder = mock(SurfaceHolder.class, RETURNS_DEFAULTS);
+        MockitoAnnotations.initMocks(this);
+        prepareSurface();
+    }
+
+    @After
+    public void tearDown() {
+        mSurfaceHolder.getSurface().destroy();
+        mSurfaceHolder = null;
+    }
+
+    private void prepareSurface() {
+        final SurfaceSession session = new SurfaceSession();
+        final SurfaceControl control = new SurfaceControl.Builder(session)
+                .setName("Test")
+                .setBufferSize(100, 100)
+                .setFormat(PixelFormat.RGB_888)
+                .build();
+        final Surface surface = new Surface();
+        surface.copyFrom(control);
+        when(mSurfaceHolder.getSurface()).thenReturn(surface);
+        assertThat(mSurfaceHolder.getSurface()).isNotNull();
+        assertThat(mSurfaceHolder.getSurface().isValid()).isTrue();
     }
 
     @Test
     public void testInit_finish() {
         mEglHelper.init(mSurfaceHolder, false /* wideColorGamut */);
+        assertThat(mEglHelper.hasEglDisplay()).isTrue();
+        assertThat(mEglHelper.hasEglContext()).isTrue();
+        assertThat(mEglHelper.hasEglSurface()).isTrue();
+        verify(mEglHelper).askCreatingEglWindowSurface(
+                any(SurfaceHolder.class), eq(null), anyInt());
+
+        mEglHelper.finish();
+        assertThat(mEglHelper.hasEglSurface()).isFalse();
+        assertThat(mEglHelper.hasEglContext()).isFalse();
+        assertThat(mEglHelper.hasEglDisplay()).isFalse();
+    }
+
+    @Test
+    public void testInit_finish_wide_gamut() {
+        // In EglHelper, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490;
+        doReturn(0x3490).when(mEglHelper).getWcgCapability();
+        // In EglHelper, KHR_GL_COLOR_SPACE = "EGL_KHR_gl_colorspace";
+        doReturn(true).when(mEglHelper).checkExtensionCapability("EGL_KHR_gl_colorspace");
+        ArgumentCaptor<int[]> ac = ArgumentCaptor.forClass(int[].class);
+        // {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT, EGL_NONE}
+        final int[] expectedArgument = new int[] {0x309D, 0x3490, 0x3038};
+
+        mEglHelper.init(mSurfaceHolder, true /* wideColorGamut */);
+        verify(mEglHelper)
+                .askCreatingEglWindowSurface(any(SurfaceHolder.class), ac.capture(), anyInt());
+        assertThat(ac.getValue()).isNotNull();
+        assertThat(ac.getValue()).isEqualTo(expectedArgument);
         mEglHelper.finish();
     }
 
     @Test
     public void testFinish_shouldNotCrash() {
-        assertFalse(mEglHelper.hasEglDisplay());
-        assertFalse(mEglHelper.hasEglSurface());
-        assertFalse(mEglHelper.hasEglContext());
+        mEglHelper.terminateEglDisplay();
+        assertThat(mEglHelper.hasEglDisplay()).isFalse();
+        assertThat(mEglHelper.hasEglSurface()).isFalse();
+        assertThat(mEglHelper.hasEglContext()).isFalse();
 
         mEglHelper.finish();
+        verify(mEglHelper, never()).destroyEglContext();
+        verify(mEglHelper, never()).destroyEglSurface();
+        verify(mEglHelper, atMost(1)).terminateEglDisplay();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
new file mode 100644
index 0000000..d881fd5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageWallpaperRendererTest.java
@@ -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.glwallpaper;
+
+import static com.android.systemui.glwallpaper.GLWallpaperRenderer.SurfaceProxy;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.app.WallpaperManager;
+import android.app.WallpaperManager.ColorManagementProxy;
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+public class ImageWallpaperRendererTest extends SysuiTestCase {
+
+    private WallpaperManager mWpmSpy;
+    private SurfaceProxy mSurfaceProxy;
+
+    @Before
+    public void setUp() throws Exception {
+        final WallpaperManager wpm = mContext.getSystemService(WallpaperManager.class);
+        mWpmSpy = spy(wpm);
+        mContext.addMockSystemService(WallpaperManager.class, mWpmSpy);
+
+        mSurfaceProxy = new SurfaceProxy() {
+            @Override
+            public void requestRender() {
+                // NO-op
+            }
+
+            @Override
+            public void preRender() {
+                // No-op
+            }
+
+            @Override
+            public void postRender() {
+                // No-op
+            }
+        };
+    }
+
+    @Test
+    public void testWcgContent() throws IOException {
+        final Bitmap srgbBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
+        final Bitmap p3Bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888,
+                false /* hasAlpha */, ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
+
+        final ColorManagementProxy proxy = new ColorManagementProxy(mContext);
+        final ColorManagementProxy cmProxySpy = spy(proxy);
+        final Set<ColorSpace> supportedWideGamuts = new HashSet<>();
+        supportedWideGamuts.add(ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
+
+        try {
+            doReturn(true).when(mWpmSpy).shouldEnableWideColorGamut();
+            doReturn(cmProxySpy).when(mWpmSpy).getColorManagementProxy();
+            doReturn(supportedWideGamuts).when(cmProxySpy).getSupportedColorSpaces();
+
+            mWpmSpy.setBitmap(p3Bitmap);
+            ImageWallpaperRenderer rendererP3 = new ImageWallpaperRenderer(mContext, mSurfaceProxy);
+            assertThat(rendererP3.isWcgContent()).isTrue();
+
+            mWpmSpy.setBitmap(srgbBitmap);
+            ImageWallpaperRenderer renderer = new ImageWallpaperRenderer(mContext, mSurfaceProxy);
+            assertThat(renderer.isWcgContent()).isFalse();
+        } finally {
+            srgbBitmap.recycle();
+            p3Bitmap.recycle();
+        }
+    }
+
+}
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 3439fe5..d26ae6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityControllerTest.java
@@ -119,7 +119,6 @@
         doReturn(code).when(mIActivityTaskManager).startActivityAsUser(
                 eq((IApplicationThread) null),
                 eq((String) null),
-                eq((String) null),
                 any(Intent.class),
                 eq((String) null),
                 eq((IBinder) null),
@@ -135,7 +134,6 @@
         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/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 07f6936..5a0a495 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -58,10 +58,13 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.statusbar.NotificationVisibility;
+import com.android.internal.util.NotificationMessagingUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -83,12 +86,13 @@
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
 import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.RowContentBindParams;
 import com.android.systemui.statusbar.notification.row.RowContentBindStage;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
-import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
+import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -96,6 +100,7 @@
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.util.Assert;
 import com.android.systemui.util.leak.LeakDetector;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.After;
 import org.junit.Before;
@@ -106,6 +111,7 @@
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -141,8 +147,13 @@
     @Mock private NotificationEntryManagerLogger mLogger;
     @Mock private FeatureFlags mFeatureFlags;
     @Mock private LeakDetector mLeakDetector;
-    @Mock private ActivatableNotificationViewController mActivatableNotificationViewController;
-    @Mock private NotificationRowComponent.Builder mNotificationRowComponentBuilder;
+    @Mock private NotificationMediaManager mNotificationMediaManager;
+    @Mock private ExpandableNotificationRowComponent.Builder
+            mExpandableNotificationRowComponentBuilder;
+    @Mock private ExpandableNotificationRowComponent mExpandableNotificationRowComponent;
+    @Mock private FalsingManager mFalsingManager;
+    @Mock private KeyguardBypassController mKeyguardBypassController;
+    @Mock private StatusBarStateController mStatusBarStateController;
 
     private int mId;
     private NotificationEntry mEntry;
@@ -191,7 +202,6 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mDependency.injectMockDependency(SmartReplyController.class);
-        mDependency.injectMockDependency(NotificationMediaManager.class);
 
         mCountDownLatch = new CountDownLatch(1);
 
@@ -207,28 +217,23 @@
 
         mEntry.expandedIcon = mock(StatusBarIconView.class);
 
-        when(mNotificationRowComponentBuilder.activatableNotificationView(any()))
-                .thenReturn(mNotificationRowComponentBuilder);
-        when(mNotificationRowComponentBuilder.build()).thenReturn(
-                () -> mActivatableNotificationViewController);
-
         RowContentBindStage bindStage = mock(RowContentBindStage.class);
         when(bindStage.getStageParams(any())).thenReturn(new RowContentBindParams());
-
         NotificationRowBinderImpl notificationRowBinder =
                 new NotificationRowBinderImpl(mContext,
+                        new NotificationMessagingUtil(mContext),
                         mRemoteInputManager,
                         mLockscreenUserManager,
                         mock(NotifBindPipeline.class),
                         bindStage,
                         true, /* allowLongPress */
-                        mock(KeyguardBypassController.class),
-                        mock(StatusBarStateController.class),
+                        mKeyguardBypassController,
+                        mStatusBarStateController,
                         mGroupManager,
                         mGutsManager,
                         mNotificationInterruptionStateProvider,
-                        () -> new RowInflaterTask(mNotificationRowComponentBuilder),
-                        mock(NotificationLogger.class));
+                        RowInflaterTask::new,
+                        mExpandableNotificationRowComponentBuilder);
 
         when(mFeatureFlags.isNewNotifPipelineEnabled()).thenReturn(false);
         when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
@@ -236,7 +241,7 @@
                 mLogger,
                 mGroupManager,
                 new NotificationRankingManager(
-                        () -> mock(NotificationMediaManager.class),
+                        () -> mNotificationMediaManager,
                         mGroupManager,
                         mHeadsUpManager,
                         mock(NotificationFilter.class),
@@ -255,13 +260,55 @@
         mEntryManager.addNotificationEntryListener(mEntryListener);
         mEntryManager.addNotificationRemoveInterceptor(mRemoveInterceptor);
 
-        notificationRowBinder.setUpWithPresenter(
-                mPresenter, mListContainer, mHeadsUpManager, mBindCallback);
+        notificationRowBinder.setUpWithPresenter(mPresenter, mListContainer, mBindCallback);
         notificationRowBinder.setInflationCallback(mEntryManager);
         notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
 
         setUserSentiment(
                 mEntry.getKey(), Ranking.USER_SENTIMENT_NEUTRAL);
+
+        ArgumentCaptor<ExpandableNotificationRow> viewCaptor =
+                ArgumentCaptor.forClass(ExpandableNotificationRow.class);
+        when(mExpandableNotificationRowComponentBuilder
+                .expandableNotificationRow(viewCaptor.capture()))
+                .thenReturn(mExpandableNotificationRowComponentBuilder);
+        when(mExpandableNotificationRowComponentBuilder
+                .notificationEntry(any()))
+                .thenReturn(mExpandableNotificationRowComponentBuilder);
+        when(mExpandableNotificationRowComponentBuilder
+                .onDismissRunnable(any()))
+                .thenReturn(mExpandableNotificationRowComponentBuilder);
+        when(mExpandableNotificationRowComponentBuilder
+                .inflationCallback(any()))
+                .thenReturn(mExpandableNotificationRowComponentBuilder);
+        when(mExpandableNotificationRowComponentBuilder
+                .onExpandClickListener(any()))
+                .thenReturn(mExpandableNotificationRowComponentBuilder);
+
+        when(mExpandableNotificationRowComponentBuilder.build())
+                .thenReturn(mExpandableNotificationRowComponent);
+        when(mExpandableNotificationRowComponent.getExpandableNotificationRowController())
+                .thenAnswer((Answer<ExpandableNotificationRowController>) invocation ->
+                        new ExpandableNotificationRowController(
+                                viewCaptor.getValue(),
+                                mock(ActivatableNotificationViewController.class),
+                                mNotificationMediaManager,
+                                mock(PluginManager.class),
+                                new FakeSystemClock(),
+                                "FOOBAR", "FOOBAR",
+                                mKeyguardBypassController,
+                                mGroupManager,
+                                bindStage,
+                                mock(NotificationLogger.class),
+                                mHeadsUpManager,
+                                mPresenter,
+                                mStatusBarStateController,
+                                mEntryManager,
+                                mGutsManager,
+                                true,
+                                null,
+                                mFalsingManager
+                        ));
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index d8cf6ed..a891810 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -194,9 +194,8 @@
     @Test
     public void testClickSound() throws Exception {
         assertTrue("Should play sounds by default.", mGroupRow.isSoundEffectsEnabled());
-        StatusBarStateController mock = mock(StatusBarStateController.class);
+        StatusBarStateController mock = mNotificationTestHelper.getStatusBarStateController();
         when(mock.isDozing()).thenReturn(true);
-        mGroupRow.setStatusBarStateController(mock);
         mGroupRow.setSecureStateProvider(()-> false);
         assertFalse("Shouldn't play sounds when dark and trusted.",
                 mGroupRow.isSoundEffectsEnabled());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index f080d67..e8de10f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -75,6 +75,7 @@
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.phone.ShadeController;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -131,6 +132,8 @@
     private ShortcutManager mShortcutManager;
     @Mock
     private NotificationGuts mNotificationGuts;
+    @Mock
+    private ShadeController mShadeController;
 
     @Before
     public void setUp() throws Exception {
@@ -139,12 +142,12 @@
         mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
         mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
         mDependency.injectTestDependency(BubbleController.class, mBubbleController);
+        mDependency.injectTestDependency(ShadeController.class, mShadeController);
         // Inflate the layout
         final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
         mNotificationInfo = (NotificationConversationInfo) layoutInflater.inflate(
                 R.layout.notification_conversation_info,
                 null);
-        mNotificationInfo.mShowHomeScreen = true;
         mNotificationInfo.setGutsParent(mNotificationGuts);
         doAnswer((Answer<Object>) invocation -> {
             mNotificationInfo.handleCloseControls(true, false);
@@ -173,7 +176,7 @@
         when(mShortcutInfo.getShortLabel()).thenReturn("Convo name");
         List<ShortcutInfo> shortcuts = Arrays.asList(mShortcutInfo);
         when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
-        mImage = mContext.getDrawable(R.drawable.ic_star);
+        mImage = mContext.getDrawable(R.drawable.ic_remove);
         when(mLauncherApps.getShortcutBadgedIconDrawable(eq(mShortcutInfo),
                 anyInt())).thenReturn(mImage);
 
@@ -333,8 +336,6 @@
                 true);
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(GONE, nameView.getVisibility());
-        final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
-        assertEquals(GONE, dividerView.getVisibility());
     }
 
     @Test
@@ -364,8 +365,6 @@
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(VISIBLE, nameView.getVisibility());
         assertTrue(nameView.getText().toString().contains("Proxied"));
-        final TextView dividerView = mNotificationInfo.findViewById(R.id.pkg_divider);
-        assertEquals(VISIBLE, dividerView.getVisibility());
     }
 
     @Test
@@ -502,6 +501,7 @@
         verify(mShortcutManager, times(1)).requestPinShortcut(mShortcutInfo, null);
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), anyInt(), any());
+        verify(mShadeController).animateCollapsePanels();
     }
 
     @Test
@@ -644,9 +644,9 @@
                 true);
 
 
-        Button fave = mNotificationInfo.findViewById(R.id.fave);
-        assertEquals(mContext.getString(R.string.notification_conversation_favorite),
-                fave.getText().toString());
+        ImageButton fave = mNotificationInfo.findViewById(R.id.fave);
+        assertEquals(mContext.getString(R.string.notification_conversation_unfavorite),
+                fave.getContentDescription().toString());
 
         fave.performClick();
         mTestableLooper.processAllMessages();
@@ -677,9 +677,9 @@
                 null,
                 true);
 
-        Button fave = mNotificationInfo.findViewById(R.id.fave);
-        assertEquals(mContext.getString(R.string.notification_conversation_unfavorite),
-                fave.getText().toString());
+        ImageButton fave = mNotificationInfo.findViewById(R.id.fave);
+        assertEquals(mContext.getString(R.string.notification_conversation_favorite),
+                fave.getContentDescription().toString());
 
         fave.performClick();
         mTestableLooper.processAllMessages();
@@ -692,34 +692,6 @@
     }
 
     @Test
-    public void testDemote() throws Exception {
-        mNotificationInfo.bindNotification(
-                mShortcutManager,
-                mLauncherApps,
-                mMockPackageManager,
-                mMockINotificationManager,
-                mVisualStabilityManager,
-                TEST_PACKAGE_NAME,
-                mNotificationChannel,
-                mEntry,
-                null,
-                null,
-                null,
-                true);
-
-
-        ImageButton demote = mNotificationInfo.findViewById(R.id.demote);
-        demote.performClick();
-        mTestableLooper.processAllMessages();
-
-        ArgumentCaptor<NotificationChannel> captor =
-                ArgumentCaptor.forClass(NotificationChannel.class);
-        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
-                anyString(), anyInt(), captor.capture());
-        assertTrue(captor.getValue().isDemoted());
-    }
-
-    @Test
     public void testMute_mute() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_DEFAULT);
         mConversationChannel.setImportance(IMPORTANCE_DEFAULT);
@@ -738,9 +710,9 @@
                 null,
                 true);
 
-        Button mute = mNotificationInfo.findViewById(R.id.mute);
-        assertEquals(mContext.getString(R.string.notification_conversation_mute),
-                mute.getText().toString());
+        ImageButton mute = mNotificationInfo.findViewById(R.id.mute);
+        assertEquals(mContext.getString(R.string.notification_conversation_unmute),
+                mute.getContentDescription().toString());
 
         mute.performClick();
         mTestableLooper.processAllMessages();
@@ -774,9 +746,9 @@
                 true);
 
 
-        Button mute = mNotificationInfo.findViewById(R.id.mute);
-        assertEquals(mContext.getString(R.string.notification_conversation_unmute),
-                mute.getText().toString());
+        ImageButton mute = mNotificationInfo.findViewById(R.id.mute);
+        assertEquals(mContext.getString(R.string.notification_conversation_mute),
+                mute.getContentDescription().toString());
 
         mute.performClick();
         mTestableLooper.processAllMessages();
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 9b2e0c3..35b5508 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
@@ -45,6 +45,7 @@
 import com.android.systemui.TestableDependency;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.bubbles.BubblesTestActivity;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -91,6 +92,7 @@
     private final NotifBindPipeline mBindPipeline;
     private final NotificationEntryListener mBindPipelineEntryListener;
     private final RowContentBindStage mBindStage;
+    private StatusBarStateController mStatusBarStateController;
 
     public NotificationTestHelper(Context context, TestableDependency dependency) {
         mContext = context;
@@ -98,9 +100,9 @@
         dependency.injectMockDependency(BubbleController.class);
         dependency.injectMockDependency(NotificationShadeWindowController.class);
         dependency.injectMockDependency(SmartReplyController.class);
-        StatusBarStateController stateController = mock(StatusBarStateController.class);
-        mGroupManager = new NotificationGroupManager(stateController);
-        mHeadsUpManager = new HeadsUpManagerPhone(mContext, stateController,
+        mStatusBarStateController = mock(StatusBarStateController.class);
+        mGroupManager = new NotificationGroupManager(mStatusBarStateController);
+        mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarStateController,
                 mock(KeyguardBypassController.class));
         mHeadsUpManager.setUp(null, mGroupManager, null, null);
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
@@ -321,6 +323,10 @@
         return notificationBuilder.build();
     }
 
+    public StatusBarStateController getStatusBarStateController() {
+        return mStatusBarStateController;
+    }
+
     private ExpandableNotificationRow generateRow(
             Notification notification,
             String pkg,
@@ -382,7 +388,11 @@
                 mGroupManager,
                 mHeadsUpManager,
                 mBindStage,
-                mock(OnExpandClickListener.class));
+                mock(OnExpandClickListener.class),
+                mock(NotificationMediaManager.class),
+                mock(ExpandableNotificationRow.OnAppOpsClickListener.class),
+                mock(FalsingManager.class),
+                mStatusBarStateController);
         row.setAboveShelfChangedListener(aboveShelf -> { });
         mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
         inflateAndWait(entry, mBindStage);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index e84f14a..2d1bc78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -30,7 +30,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -59,8 +58,6 @@
     private ExpandableNotificationRow mFirst;
     private ExpandableNotificationRow mSecond;
     @Mock
-    private StatusBarStateController mStatusBarStateController;
-    @Mock
     private KeyguardBypassController mBypassController;
 
     @Before
@@ -150,13 +147,12 @@
                 createSection(mFirst, mSecond),
                 createSection(null, null)
         });
-        ExpandableNotificationRow row = new NotificationTestHelper(getContext(), mDependency)
-                .createRow();
+        NotificationTestHelper testHelper = new NotificationTestHelper(getContext(), mDependency);
+        ExpandableNotificationRow row = testHelper.createRow();
         NotificationEntry entry = mock(NotificationEntry.class);
         when(entry.getRow()).thenReturn(row);
 
-        when(mStatusBarStateController.isDozing()).thenReturn(true);
-        row.setStatusBarStateController(mStatusBarStateController);
+        when(testHelper.getStatusBarStateController().isDozing()).thenReturn(true);
         row.setHeadsUp(true);
         mRoundnessManager.onHeadsUpStateChanged(entry, true);
         Assert.assertEquals(1f, row.getCurrentBottomRoundness(), 0.0f);
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index cf2b1f06..4efe934 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -28,6 +28,7 @@
         "netd_aidl_interface-unstable-java",
         "netlink-client",
         "networkstack-aidl-interfaces-unstable-java",
+        "android.hardware.tetheroffload.config-V1.0-java",
         "android.hardware.tetheroffload.control-V1.0-java",
         "net-utils-framework-common",
     ],
@@ -48,26 +49,26 @@
 // Due to b/143733063, APK can't access a jni lib that is in APEX (but not in the APK).
 cc_library {
     name: "libtetherutilsjni",
+    sdk_version: "current",
     srcs: [
         "jni/android_net_util_TetheringUtils.cpp",
     ],
     shared_libs: [
-        "libcgrouprc",
-        "libnativehelper_compat_libc++",
-        "libvndksupport",
-    ],
-    static_libs: [
-        "android.hardware.tetheroffload.config@1.0",
         "liblog",
-        "libbase",
-        "libbinderthreadstate",
-        "libcutils",
-        "libhidlbase",
-        "libjsoncpp",
-        "libprocessgroup",
-        "libutils",
+        "libnativehelper_compat_libc++",
     ],
 
+    // We cannot use plain "libc++" here to link libc++ dynamically because it results in:
+    //   java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not found
+    // even if "libc++" is added into jni_libs below. Adding "libc++_shared" into jni_libs doesn't
+    // build because soong complains of:
+    //   module Tethering missing dependencies: libc++_shared
+    //
+    // So, link libc++ statically. This means that we also need to ensure that all the C++ libraries
+    // we depend on do not dynamically link libc++. This is currently the case, because liblog is
+    // C-only and libnativehelper_compat_libc also uses stl: "c++_static".
+    stl: "c++_static",
+
     cflags: [
         "-Wall",
         "-Werror",
@@ -86,9 +87,8 @@
     // Build system doesn't track transitive dependeicies for jni_libs, list all the dependencies
     // explicitly.
     jni_libs: [
-        "libcgrouprc",
+        "liblog",
         "libnativehelper_compat_libc++",
-        "libvndksupport",
         "libtetherutilsjni",
     ],
     resource_dirs: [
diff --git a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
index 1cf8f98..54934406 100644
--- a/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
+++ b/packages/Tethering/jni/android_net_util_TetheringUtils.cpp
@@ -16,123 +16,18 @@
 
 #include <errno.h>
 #include <error.h>
-#include <hidl/HidlSupport.h>
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
 #include <nativehelper/ScopedUtfChars.h>
-#include <linux/netfilter/nfnetlink.h>
-#include <linux/netlink.h>
 #include <net/if.h>
 #include <netinet/icmp6.h>
 #include <sys/socket.h>
-#include <android-base/unique_fd.h>
-#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
 
 #define LOG_TAG "TetheringUtils"
-#include <utils/Log.h>
+#include <android/log.h>
 
 namespace android {
 
-using hardware::hidl_handle;
-using hardware::hidl_string;
-using hardware::tetheroffload::config::V1_0::IOffloadConfig;
-
-namespace {
-
-inline const sockaddr * asSockaddr(const sockaddr_nl *nladdr) {
-    return reinterpret_cast<const sockaddr *>(nladdr);
-}
-
-int conntrackSocket(unsigned groups) {
-    base::unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER));
-    if (s.get() < 0) return -errno;
-
-    const struct sockaddr_nl bind_addr = {
-        .nl_family = AF_NETLINK,
-        .nl_pad = 0,
-        .nl_pid = 0,
-        .nl_groups = groups,
-    };
-    if (bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) != 0) {
-        return -errno;
-    }
-
-    const struct sockaddr_nl kernel_addr = {
-        .nl_family = AF_NETLINK,
-        .nl_pad = 0,
-        .nl_pid = 0,
-        .nl_groups = groups,
-    };
-    if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
-        return -errno;
-    }
-
-    return s.release();
-}
-
-// Return a hidl_handle that owns the file descriptor owned by fd, and will
-// auto-close it (otherwise there would be double-close problems).
-//
-// Rely upon the compiler to eliminate the constexprs used for clarity.
-hidl_handle handleFromFileDescriptor(base::unique_fd fd) {
-    hidl_handle h;
-
-    static constexpr int kNumFds = 1;
-    static constexpr int kNumInts = 0;
-    native_handle_t *nh = native_handle_create(kNumFds, kNumInts);
-    nh->data[0] = fd.release();
-
-    static constexpr bool kTakeOwnership = true;
-    h.setTo(nh, kTakeOwnership);
-
-    return h;
-}
-
-}  // namespace
-
-static jboolean android_net_util_configOffload(
-        JNIEnv* /* env */) {
-    sp<IOffloadConfig> configInterface = IOffloadConfig::getService();
-    if (configInterface.get() == nullptr) {
-        ALOGD("Could not find IOffloadConfig service.");
-        return false;
-    }
-
-    // Per the IConfigOffload definition:
-    //
-    // fd1   A file descriptor bound to the following netlink groups
-    //       (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
-    //
-    // fd2   A file descriptor bound to the following netlink groups
-    //       (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
-    base::unique_fd
-            fd1(conntrackSocket(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY)),
-            fd2(conntrackSocket(NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY));
-    if (fd1.get() < 0 || fd2.get() < 0) {
-        ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
-        return false;
-    }
-
-    hidl_handle h1(handleFromFileDescriptor(std::move(fd1))),
-                h2(handleFromFileDescriptor(std::move(fd2)));
-
-    bool rval(false);
-    hidl_string msg;
-    const auto status = configInterface->setHandles(h1, h2,
-            [&rval, &msg](bool success, const hidl_string& errMsg) {
-                rval = success;
-                msg = errMsg;
-            });
-    if (!status.isOk() || !rval) {
-        ALOGE("IOffloadConfig::setHandles() error: '%s' / '%s'",
-              status.description().c_str(), msg.c_str());
-        // If status is somehow not ok, make sure rval captures this too.
-        rval = false;
-    }
-
-    return rval;
-}
-
 static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
         jint ifIndex)
 {
@@ -229,7 +124,6 @@
  */
 static const JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
-    { "configOffload", "()Z", (void*) android_net_util_configOffload },
     { "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_util_setupRaSocket },
 };
 
@@ -242,7 +136,7 @@
 extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
     JNIEnv *env;
     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
-        ALOGE("ERROR: GetEnv failed");
+        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed");
         return JNI_ERR;
     }
 
diff --git a/packages/Tethering/src/android/net/util/TetheringUtils.java b/packages/Tethering/src/android/net/util/TetheringUtils.java
index fa543bd..5a6d5c1 100644
--- a/packages/Tethering/src/android/net/util/TetheringUtils.java
+++ b/packages/Tethering/src/android/net/util/TetheringUtils.java
@@ -24,14 +24,6 @@
  * {@hide}
  */
 public class TetheringUtils {
-
-    /**
-     * Offload management process need to know conntrack rules to support NAT, but it may not have
-     * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and
-     * share them with offload management process.
-     */
-    public static native boolean configOffload();
-
     /**
      * Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
      * @param fd the socket's {@link FileDescriptor}.
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 90b9d3f..b545717 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -18,19 +18,28 @@
 
 import static android.net.util.TetheringUtils.uint16;
 
+import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
 import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
 import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
 import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
 import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
 import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
+import android.net.netlink.NetlinkSocket;
 import android.net.util.SharedLog;
-import android.net.util.TetheringUtils;
+import android.net.util.SocketUtils;
 import android.os.Handler;
+import android.os.NativeHandle;
 import android.os.RemoteException;
+import android.system.ErrnoException;
+import android.system.Os;
 import android.system.OsConstants;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.net.SocketException;
 import java.util.ArrayList;
 
 
@@ -49,6 +58,10 @@
     private static final String NO_INTERFACE_NAME = "";
     private static final String NO_IPV4_ADDRESS = "";
     private static final String NO_IPV4_GATEWAY = "";
+    // Reference kernel/uapi/linux/netfilter/nfnetlink_compat.h
+    private static final int NF_NETLINK_CONNTRACK_NEW = 1;
+    private static final int NF_NETLINK_CONNTRACK_UPDATE = 2;
+    private static final int NF_NETLINK_CONNTRACK_DESTROY = 4;
 
     private final Handler mHandler;
     private final SharedLog mLog;
@@ -121,9 +134,103 @@
         return DEFAULT_TETHER_OFFLOAD_DISABLED;
     }
 
-    /** Configure offload management process. */
+    /**
+     * Offload management process need to know conntrack rules to support NAT, but it may not have
+     * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and
+     * share them with offload management process.
+     */
     public boolean initOffloadConfig() {
-        return TetheringUtils.configOffload();
+        IOffloadConfig offloadConfig;
+        try {
+            offloadConfig = IOffloadConfig.getService();
+        } catch (RemoteException e) {
+            mLog.e("getIOffloadConfig error " + e);
+            return false;
+        }
+        if (offloadConfig == null) {
+            mLog.e("Could not find IOffloadConfig service");
+            return false;
+        }
+        // Per the IConfigOffload definition:
+        //
+        // h1    provides a file descriptor bound to the following netlink groups
+        //       (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
+        //
+        // h2    provides a file descriptor bound to the following netlink groups
+        //       (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
+        final NativeHandle h1 = createConntrackSocket(
+                NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY);
+        if (h1 == null) return false;
+
+        final NativeHandle h2 = createConntrackSocket(
+                NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY);
+        if (h2 == null) {
+            closeFdInNativeHandle(h1);
+            return false;
+        }
+
+        final CbResults results = new CbResults();
+        try {
+            offloadConfig.setHandles(h1, h2,
+                    (boolean success, String errMsg) -> {
+                        results.mSuccess = success;
+                        results.mErrMsg = errMsg;
+                    });
+        } catch (RemoteException e) {
+            record("initOffloadConfig, setHandles fail", e);
+            return false;
+        }
+        // Explicitly close FDs.
+        closeFdInNativeHandle(h1);
+        closeFdInNativeHandle(h2);
+
+        record("initOffloadConfig, setHandles results:", results);
+        return results.mSuccess;
+    }
+
+    private void closeFdInNativeHandle(final NativeHandle h) {
+        try {
+            h.close();
+        } catch (IOException | IllegalStateException e) {
+            // IllegalStateException means fd is already closed, do nothing here.
+            // Also nothing we can do if IOException.
+        }
+    }
+
+    private NativeHandle createConntrackSocket(final int groups) {
+        FileDescriptor fd;
+        try {
+            fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER);
+        } catch (ErrnoException e) {
+            mLog.e("Unable to create conntrack socket " + e);
+            return null;
+        }
+
+        final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups);
+        try {
+            Os.bind(fd, sockAddr);
+        } catch (ErrnoException | SocketException e) {
+            mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e);
+            try {
+                SocketUtils.closeSocket(fd);
+            } catch (IOException ie) {
+                // Nothing we can do here
+            }
+            return null;
+        }
+        try {
+            Os.connect(fd, sockAddr);
+        } catch (ErrnoException | SocketException e) {
+            mLog.e("connect to kernel fail for groups " + groups + " error: " + e);
+            try {
+                SocketUtils.closeSocket(fd);
+            } catch (IOException ie) {
+                // Nothing we can do here
+            }
+            return null;
+        }
+
+        return new NativeHandle(fd, true);
     }
 
     /** Initialize the tethering offload HAL. */
diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java
index c67fe9f..1e8109c 100644
--- a/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java
+++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacNative.java
@@ -23,6 +23,8 @@
 public class PacNative {
     private static final String TAG = "PacProxy";
 
+    private static final PacNative sInstance = new PacNative();
+
     private String mCurrentPac;
 
     private boolean mIsActive;
@@ -39,10 +41,14 @@
         System.loadLibrary("jni_pacprocessor");
     }
 
-    PacNative() {
+    private PacNative() {
 
     }
 
+    public static PacNative getInstance() {
+        return sInstance;
+    }
+
     public synchronized boolean startPacSupport() {
         if (createV8ParserNativeLocked()) {
             Log.e(TAG, "Unable to Create v8 Proxy Parser.");
diff --git a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
index 74391eb..b006d6e 100644
--- a/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
+++ b/packages/services/PacProcessor/src/com/android/pacprocessor/PacService.java
@@ -31,43 +31,27 @@
 public class PacService extends Service {
     private static final String TAG = "PacService";
 
-    private PacNative mPacNative;
-    private ProxyServiceStub mStub;
+    private PacNative mPacNative = PacNative.getInstance();
+    private ProxyServiceStub mStub = new ProxyServiceStub();
 
     @Override
     public void onCreate() {
         super.onCreate();
-        if (mPacNative == null) {
-            mPacNative = new PacNative();
-            mStub = new ProxyServiceStub(mPacNative);
-        }
+        mPacNative.startPacSupport();
     }
 
     @Override
     public void onDestroy() {
+        mPacNative.stopPacSupport();
         super.onDestroy();
-        if (mPacNative != null) {
-            mPacNative.stopPacSupport();
-            mPacNative = null;
-            mStub = null;
-        }
     }
 
     @Override
     public IBinder onBind(Intent intent) {
-        if (mPacNative == null) {
-            mPacNative = new PacNative();
-            mStub = new ProxyServiceStub(mPacNative);
-        }
         return mStub;
     }
 
-    private static class ProxyServiceStub extends IProxyService.Stub {
-        private final PacNative mPacNative;
-
-        public ProxyServiceStub(PacNative pacNative) {
-            mPacNative = pacNative;
-        }
+    private class ProxyServiceStub extends IProxyService.Stub {
 
         @Override
         public String resolvePacFile(String host, String url) throws RemoteException {
@@ -102,20 +86,12 @@
 
         @Override
         public void startPacSystem() throws RemoteException {
-            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-                Log.e(TAG, "Only system user is allowed to call startPacSystem");
-                throw new SecurityException();
-            }
-            mPacNative.startPacSupport();
+            //TODO: remove
         }
 
         @Override
         public void stopPacSystem() throws RemoteException {
-            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
-                Log.e(TAG, "Only system user is allowed to call stopPacSystem");
-                throw new SecurityException();
-            }
-            mPacNative.stopPacSupport();
+            //TODO: remove
         }
     }
 }
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 54b4201..63dd99e 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -244,6 +244,10 @@
     // Package: android
     NOTE_SOFTAP_AUTO_DISABLED = 58;
 
+    // Notify the user that their admin has changed location settings.
+    // Package: android
+    NOTE_LOCATION_CHANGED = 59;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/Android.bp b/services/Android.bp
index 32394f4..416f448 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -30,6 +30,7 @@
         ":services.usb-sources",
         ":services.voiceinteraction-sources",
         ":service-permission-sources",
+        ":service-statsd-sources",
     ],
     visibility: ["//visibility:private"],
 }
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index e3d2dcc..6247a63 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -1080,12 +1080,8 @@
         }
     }
 
-    public Map<String, Set<String>> getExcludedRestoreKeys(String... packages) {
-        return mBackupPreferences.getExcludedRestoreKeysForPackages(packages);
-    }
-
-    public Map<String, Set<String>> getAllExcludedRestoreKeys() {
-        return mBackupPreferences.getAllExcludedRestoreKeys();
+    public Set<String> getExcludedRestoreKeys(String packageName) {
+        return mBackupPreferences.getExcludedRestoreKeysForPackage(packageName);
     }
 
     /** Used for generating random salts or passwords. */
@@ -3356,8 +3352,7 @@
                                 restoreSet,
                                 packageName,
                                 token,
-                                listener,
-                                getExcludedRestoreKeys(packageName));
+                                listener);
                 mBackupHandler.sendMessage(msg);
             } catch (Exception e) {
                 // Calling into the transport broke; back off and proceed with the installation.
diff --git a/services/backup/java/com/android/server/backup/UserBackupPreferences.java b/services/backup/java/com/android/server/backup/UserBackupPreferences.java
index 41b9719..bb8bf52 100644
--- a/services/backup/java/com/android/server/backup/UserBackupPreferences.java
+++ b/services/backup/java/com/android/server/backup/UserBackupPreferences.java
@@ -48,16 +48,7 @@
         mEditor.commit();
     }
 
-    Map<String, Set<String>> getExcludedRestoreKeysForPackages(String... packages) {
-        Map<String, Set<String>> excludedKeys = new HashMap<>();
-        for (String packageName : packages) {
-            excludedKeys.put(packageName,
-                    mPreferences.getStringSet(packageName, Collections.emptySet()));
-        }
-        return excludedKeys;
-    }
-
-    Map<String, Set<String>> getAllExcludedRestoreKeys() {
-        return (Map<String, Set<String>>) mPreferences.getAll();
+    Set<String> getExcludedRestoreKeysForPackage(String packageName) {
+        return mPreferences.getStringSet(packageName, Collections.emptySet());
     }
 }
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 05396f3..87a8e49 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -299,8 +299,7 @@
                                 params.pmToken,
                                 params.isSystemRestore,
                                 params.filterSet,
-                                params.listener,
-                                params.excludedKeys);
+                                params.listener);
 
                 synchronized (backupManagerService.getPendingRestores()) {
                     if (backupManagerService.isRestoreInProgress()) {
diff --git a/services/backup/java/com/android/server/backup/params/RestoreParams.java b/services/backup/java/com/android/server/backup/params/RestoreParams.java
index 09b7e35..a6fea6c 100644
--- a/services/backup/java/com/android/server/backup/params/RestoreParams.java
+++ b/services/backup/java/com/android/server/backup/params/RestoreParams.java
@@ -37,7 +37,6 @@
     public final boolean isSystemRestore;
     @Nullable public final String[] filterSet;
     public final OnTaskFinishedListener listener;
-    public final Map<String, Set<String>> excludedKeys;
 
     /**
      * No kill after restore.
@@ -48,8 +47,7 @@
             IBackupManagerMonitor monitor,
             long token,
             PackageInfo packageInfo,
-            OnTaskFinishedListener listener,
-            Map<String, Set<String>> excludedKeys) {
+            OnTaskFinishedListener listener) {
         return new RestoreParams(
                 transportClient,
                 observer,
@@ -59,8 +57,7 @@
                 /* pmToken */ 0,
                 /* isSystemRestore */ false,
                 /* filterSet */ null,
-                listener,
-                excludedKeys);
+                listener);
     }
 
     /**
@@ -73,8 +70,7 @@
             long token,
             String packageName,
             int pmToken,
-            OnTaskFinishedListener listener,
-            Map<String, Set<String>> excludedKeys) {
+            OnTaskFinishedListener listener) {
         String[] filterSet = {packageName};
         return new RestoreParams(
                 transportClient,
@@ -85,8 +81,7 @@
                 pmToken,
                 /* isSystemRestore */ false,
                 filterSet,
-                listener,
-                excludedKeys);
+                listener);
     }
 
     /**
@@ -97,8 +92,7 @@
             IRestoreObserver observer,
             IBackupManagerMonitor monitor,
             long token,
-            OnTaskFinishedListener listener,
-            Map<String, Set<String>> excludedKeys) {
+            OnTaskFinishedListener listener) {
         return new RestoreParams(
                 transportClient,
                 observer,
@@ -108,8 +102,7 @@
                 /* pmToken */ 0,
                 /* isSystemRestore */ true,
                 /* filterSet */ null,
-                listener,
-                excludedKeys);
+                listener);
     }
 
     /**
@@ -122,8 +115,7 @@
             long token,
             String[] filterSet,
             boolean isSystemRestore,
-            OnTaskFinishedListener listener,
-            Map<String, Set<String>> excludedKeys) {
+            OnTaskFinishedListener listener) {
         return new RestoreParams(
                 transportClient,
                 observer,
@@ -133,8 +125,7 @@
                 /* pmToken */ 0,
                 isSystemRestore,
                 filterSet,
-                listener,
-                excludedKeys);
+                listener);
     }
 
     private RestoreParams(
@@ -146,8 +137,7 @@
             int pmToken,
             boolean isSystemRestore,
             @Nullable String[] filterSet,
-            OnTaskFinishedListener listener,
-            Map<String, Set<String>> excludedKeys) {
+            OnTaskFinishedListener listener) {
         this.transportClient = transportClient;
         this.observer = observer;
         this.monitor = monitor;
@@ -157,6 +147,5 @@
         this.isSystemRestore = isSystemRestore;
         this.filterSet = filterSet;
         this.listener = listener;
-        this.excludedKeys = excludedKeys;
     }
 }
diff --git a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
index c0f76c3..5a57cdc 100644
--- a/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
+++ b/services/backup/java/com/android/server/backup/restore/ActiveRestoreSession.java
@@ -178,8 +178,7 @@
                                                 observer,
                                                 monitor,
                                                 token,
-                                                listener,
-                                                mBackupManagerService.getAllExcludedRestoreKeys()),
+                                                listener),
                                 "RestoreSession.restoreAll()");
                     } finally {
                         Binder.restoreCallingIdentity(oldId);
@@ -272,8 +271,7 @@
                                                 token,
                                                 packages,
                                                 /* isSystemRestore */ packages.length > 1,
-                                                listener,
-                                                mBackupManagerService.getExcludedRestoreKeys(packages)),
+                                                listener),
                                 "RestoreSession.restorePackages(" + packages.length + " packages)");
                     } finally {
                         Binder.restoreCallingIdentity(oldId);
@@ -365,8 +363,7 @@
                                     monitor,
                                     token,
                                     app,
-                                    listener,
-                                    mBackupManagerService.getExcludedRestoreKeys(app.packageName)),
+                                    listener),
                     "RestoreSession.restorePackage(" + packageName + ")");
         } finally {
             Binder.restoreCallingIdentity(oldId);
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index f90d936..3c37f73 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -155,8 +155,6 @@
     // When finished call listener
     private final OnTaskFinishedListener mListener;
 
-    private final Map<String, Set<String>> mExcludedKeys;
-
     // Key/value: bookkeeping about staged data and files for agent access
     private File mBackupDataName;
     private File mStageName;
@@ -168,14 +166,14 @@
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     @VisibleForTesting
-    PerformUnifiedRestoreTask(Map<String, Set<String>> excludedKeys) {
-        mExcludedKeys = excludedKeys;
+    PerformUnifiedRestoreTask(UserBackupManagerService backupManagerService) {
         mListener = null;
         mAgentTimeoutParameters = null;
         mTransportClient = null;
         mTransportManager = null;
         mEphemeralOpToken = 0;
         mUserId = 0;
+        this.backupManagerService = backupManagerService;
     }
 
     // This task can assume that the wakelock is properly held for it and doesn't have to worry
@@ -190,8 +188,7 @@
             int pmToken,
             boolean isFullSystemRestore,
             @Nullable String[] filterSet,
-            OnTaskFinishedListener listener,
-            Map<String, Set<String>> excludedKeys) {
+            OnTaskFinishedListener listener) {
         this.backupManagerService = backupManagerService;
         mUserId = backupManagerService.getUserId();
         mTransportManager = backupManagerService.getTransportManager();
@@ -213,8 +210,6 @@
                 backupManagerService.getAgentTimeoutParameters(),
                 "Timeout parameters cannot be null");
 
-        mExcludedKeys = excludedKeys;
-
         if (targetPackage != null) {
             // Single package restore
             mAcceptSet = new ArrayList<>();
@@ -791,8 +786,9 @@
                 !getExcludedKeysForPackage(PLATFORM_PACKAGE_NAME).isEmpty();
     }
 
-    private Set<String> getExcludedKeysForPackage(String packageName) {
-        return mExcludedKeys.getOrDefault(packageName, Collections.emptySet());
+    @VisibleForTesting
+    Set<String> getExcludedKeysForPackage(String packageName) {
+        return backupManagerService.getExcludedRestoreKeys(packageName);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 63d0924..bdcd832 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -396,7 +396,6 @@
      * @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
@@ -405,8 +404,7 @@
      */
     public abstract void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
             Intent origIntent, String resolvedType, String callingPkg,
-            @Nullable String callingFeatureId, boolean isRequesterInstantApp,
-            Bundle verificationBundle, int userId);
+            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 caacf13..687fb7d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3123,7 +3123,13 @@
         handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
     }
 
-    private void updateLingerState(NetworkAgentInfo nai, long now) {
+    /**
+     * Updates the linger state from the network requests inside the NAI.
+     * @param nai the agent info to update
+     * @param now the timestamp of the event causing this update
+     * @return whether the network was lingered as a result of this update
+     */
+    private boolean updateLingerState(@NonNull final NetworkAgentInfo nai, final long now) {
         // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
         // 2. If the network was lingering and there are now requests, unlinger it.
         // 3. If this network is unneeded (which implies it is not lingering), and there is at least
@@ -3134,12 +3140,15 @@
             nai.unlinger();
             logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
         } else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
-            int lingerTime = (int) (nai.getLingerExpiry() - now);
-            if (DBG) log("Lingering " + nai.name() + " for " + lingerTime + "ms");
+            if (DBG) {
+                final int lingerTime = (int) (nai.getLingerExpiry() - now);
+                log("Lingering " + nai.name() + " for " + lingerTime + "ms");
+            }
             nai.linger();
             logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
-            notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
+            return true;
         }
+        return false;
     }
 
     private void handleAsyncChannelHalfConnect(Message msg) {
@@ -3483,7 +3492,10 @@
                 }
                 // If there are still lingered requests on this network, don't tear it down,
                 // but resume lingering instead.
-                updateLingerState(nai, SystemClock.elapsedRealtime());
+                final long now = SystemClock.elapsedRealtime();
+                if (updateLingerState(nai, now)) {
+                    notifyNetworkLosing(nai, now);
+                }
                 if (unneeded(nai, UnneededFor.TEARDOWN)) {
                     if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
                     teardownUnneededNetwork(nai);
@@ -6620,11 +6632,16 @@
                 }
                 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));
             }
         }
+
         return reassignedRequests;
     }
 
@@ -6663,41 +6680,39 @@
             final NetworkRequestInfo nri = entry.getKey();
             final NetworkAgentInfo previousSatisfier = nri.mSatisfier;
             final NetworkAgentInfo newSatisfier = entry.getValue();
-            changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
-                    nri, previousSatisfier, newSatisfier));
-            if (newSatisfier != null) {
-                if (VDBG) log("rematch for " + newSatisfier.name());
-                if (previousSatisfier != null) {
-                    if (VDBG || DDBG) {
-                        log("   accepting network in place of " + previousSatisfier.name());
-                    }
-                    previousSatisfier.removeRequest(nri.request.requestId);
-                    previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs);
-                } else {
-                    if (VDBG || DDBG) log("   accepting network in place of null");
-                }
-                newSatisfier.unlingerRequest(nri.request);
-                if (!newSatisfier.addRequest(nri.request)) {
-                    Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
-                }
-            } else {
-                // If "newNetwork" is listed as satisfying "nri" but no longer satisfies "nri",
-                // mark it as no longer satisfying "nri".  Because networks are processed by
-                // rematchAllNetworksAndRequests() in descending score order, "currentNetwork" will
-                // match "newNetwork" before this loop will encounter a "currentNetwork" with higher
-                // score than "newNetwork" and where "currentNetwork" no longer satisfies "nri".
-                // This means this code doesn't have to handle the case where "currentNetwork" no
-                // longer satisfies "nri" when "currentNetwork" does not equal "newNetwork".
-                if (DBG) {
-                    log("Network " + newNetwork.name() + " stopped satisfying" +
-                            " request " + nri.request.requestId);
-                }
-                newNetwork.removeRequest(nri.request.requestId);
-            }
-            nri.mSatisfier = newSatisfier;
+            updateSatisfiersForRematchRequest(nri, previousSatisfier, newSatisfier, now);
         }
     }
 
+    private void updateSatisfiersForRematchRequest(@NonNull final NetworkRequestInfo nri,
+            @Nullable final NetworkAgentInfo previousSatisfier,
+            @Nullable final NetworkAgentInfo newSatisfier,
+            final long now) {
+        if (newSatisfier != null) {
+            if (VDBG) log("rematch for " + newSatisfier.name());
+            if (previousSatisfier != null) {
+                if (VDBG || DDBG) {
+                    log("   accepting network in place of " + previousSatisfier.name());
+                }
+                previousSatisfier.removeRequest(nri.request.requestId);
+                previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs);
+            } else {
+                if (VDBG || DDBG) log("   accepting network in place of null");
+            }
+            newSatisfier.unlingerRequest(nri.request);
+            if (!newSatisfier.addRequest(nri.request)) {
+                Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
+            }
+        } else {
+            if (DBG) {
+                log("Network " + previousSatisfier.name() + " stopped satisfying"
+                        + " request " + nri.request.requestId);
+            }
+            previousSatisfier.removeRequest(nri.request.requestId);
+        }
+        nri.mSatisfier = newSatisfier;
+    }
+
     /**
      * Attempt to rematch all Networks with NetworkRequests.  This may result in Networks
      * being disconnected.
@@ -6773,13 +6788,20 @@
             processNewlySatisfiedListenRequests(event.mNetwork);
         }
 
+        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
             // timers. updateLingerState reads the state from the network agent and does nothing
             // if the state has not changed : the source of truth is controlled with
             // NetworkAgentInfo#lingerRequest and NetworkAgentInfo#unlingerRequest, which have been
             // called while rematching the individual networks above.
-            updateLingerState(nai, now);
+            if (updateLingerState(nai, now)) {
+                lingeredNetworks.add(nai);
+            }
+        }
+
+        for (final NetworkAgentInfo nai : lingeredNetworks) {
+            notifyNetworkLosing(nai, now);
         }
 
         updateLegacyTypeTrackerAndVpnLockdownForRematch(oldDefaultNetwork, newDefaultNetwork, nais);
@@ -6795,7 +6817,9 @@
                     // and became unneeded due to another network improving its score to the
                     // point where this network will no longer be able to satisfy any requests
                     // even if it validates.
-                    updateLingerState(nai, now);
+                    if (updateLingerState(nai, now)) {
+                        notifyNetworkLosing(nai, now);
+                    }
                 } else {
                     if (DBG) log("Reaping " + nai.name());
                     teardownUnneededNetwork(nai);
@@ -7048,6 +7072,12 @@
         callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
     }
 
+    // Notify the requests on this NAI that the network is now lingered.
+    private void notifyNetworkLosing(@NonNull final NetworkAgentInfo nai, final long now) {
+        final int lingerTime = (int) (nai.getLingerExpiry() - now);
+        notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOSING, lingerTime);
+    }
+
     /**
      * Notify of the blocked state apps with a registered callback matching a given NAI.
      *
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 63cddac..2c91a11 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -43,6 +43,7 @@
 import android.location.GeocoderParams;
 import android.location.Geofence;
 import android.location.GnssMeasurementCorrections;
+import android.location.GnssRequest;
 import android.location.IBatchedLocationCallback;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
@@ -2287,12 +2288,14 @@
     }
 
     @Override
-    public boolean addGnssMeasurementsListener(IGnssMeasurementsListener listener,
-            String packageName, String featureId, String listenerIdentifier) {
+    public boolean addGnssMeasurementsListener(@Nullable GnssRequest request,
+            IGnssMeasurementsListener listener,
+            String packageName, String featureId,
+            String listenerIdentifier) {
         Objects.requireNonNull(listenerIdentifier);
 
         return mGnssManagerService != null && mGnssManagerService.addGnssMeasurementsListener(
-                listener, packageName, featureId, listenerIdentifier);
+                request, listener, packageName, featureId, listenerIdentifier);
     }
 
     @Override
@@ -2419,6 +2422,17 @@
     }
 
     @Override
+    public void setLocationEnabledForUser(boolean enabled, int userId) {
+        if (UserHandle.getCallingUserId() != userId) {
+            mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS,
+                    null);
+        }
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS,
+                "Requires WRITE_SECURE_SETTINGS permission");
+        mSettingsHelper.setLocationEnabled(enabled, userId);
+    }
+
+    @Override
     public boolean isLocationEnabledForUser(int userId) {
         userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
                 userId, false, false, "isLocationEnabledForUser", null);
diff --git a/services/core/java/com/android/server/LocationManagerServiceUtils.java b/services/core/java/com/android/server/LocationManagerServiceUtils.java
index 372e91e..ba1c81c 100644
--- a/services/core/java/com/android/server/LocationManagerServiceUtils.java
+++ b/services/core/java/com/android/server/LocationManagerServiceUtils.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -37,22 +38,31 @@
     /**
      * Listener that can be linked to a binder.
      * @param <TListener> listener type
+     * @param <TRequest> request type
      */
-    public static class LinkedListener<TListener> extends
+    public static class LinkedListener<TRequest, TListener> extends
             LinkedListenerBase {
+        @Nullable protected final TRequest mRequest;
         private final TListener mListener;
         private final Consumer<TListener> mBinderDeathCallback;
 
         public LinkedListener(
+                @Nullable TRequest request,
                 @NonNull TListener listener,
                 String listenerName,
                 @NonNull CallerIdentity callerIdentity,
                 @NonNull Consumer<TListener> binderDeathCallback) {
             super(callerIdentity, listenerName);
             mListener = listener;
+            mRequest = request;
             mBinderDeathCallback = binderDeathCallback;
         }
 
+        @Nullable
+        public TRequest getRequest() {
+            return mRequest;
+        }
+
         @Override
         public void binderDied() {
             if (D) Log.d(TAG, "Remote " + mListenerName + " died.");
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index 135f6f3..6fb7b26 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -272,10 +272,11 @@
     private void handlePinOnStart() {
         final String bootImage = SystemProperties.get("dalvik.vm.boot-image", "");
         String[] filesToPin = null;
-        if (bootImage.endsWith("apex.art")) {
-            // Use the files listed for that specific boot image
+        if (bootImage.endsWith("boot-image.prof")) {
+            // Use the files listed for that specific boot image.
+            // TODO: find a better way to know we're using the JIT zygote configuration.
             filesToPin = mContext.getResources().getStringArray(
-                  com.android.internal.R.array.config_apexBootImagePinnerServiceFiles);
+                  com.android.internal.R.array.config_jitzygoteBootImagePinnerServiceFiles);
         } else {
             // Files to pin come from the overlay and can be specified per-device config
             filesToPin = mContext.getResources().getStringArray(
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 0eedf8a..f2d5a9b 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -302,15 +302,19 @@
     private final ContentObserver mDarkThemeObserver = new ContentObserver(mHandler) {
         @Override
         public void onChange(boolean selfChange, Uri uri) {
-            int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE,
-                    mNightMode, 0);
-            if (mode == MODE_NIGHT_AUTO || mode == MODE_NIGHT_CUSTOM) {
-                mode = MODE_NIGHT_YES;
-            }
-            SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode));
+            updateSystemProperties();
         }
     };
 
+    private void updateSystemProperties() {
+        int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE,
+                mNightMode, 0);
+        if (mode == MODE_NIGHT_AUTO || mode == MODE_NIGHT_CUSTOM) {
+            mode = MODE_NIGHT_YES;
+        }
+        SystemProperties.set(SYSTEM_PROPERTY_DEVICE_THEME, Integer.toString(mode));
+    }
+
     @Override
     public void onSwitchUser(int userHandle) {
         super.onSwitchUser(userHandle);
@@ -392,6 +396,7 @@
 
         context.getContentResolver().registerContentObserver(Secure.getUriFor(Secure.UI_NIGHT_MODE),
                 false, mDarkThemeObserver, 0);
+        updateSystemProperties();
     }
 
     @VisibleForTesting
@@ -1261,9 +1266,8 @@
             if (Sandman.shouldStartDockApp(getContext(), homeIntent)) {
                 try {
                     int result = ActivityTaskManager.getService().startActivityWithConfig(
-                            null, getContext().getBasePackageName(), getContext().getFeatureId(),
-                            homeIntent, null, null, null, 0, 0, mConfiguration, null,
-                            UserHandle.USER_CURRENT);
+                            null, getContext().getBasePackageName(), 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 97b5eaa..982466d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -417,23 +417,22 @@
     }
 
     private boolean appRestrictedAnyInBackground(final int uid, final String packageName) {
-        final int mode = mAm.getAppOpsManager().checkOpNoThrow(
+        final int mode = mAm.mAppOpsService.checkOperation(
                 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,
-            @Nullable String callingFeatureId, final int userId)
+            int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
             throws TransactionTooLargeException {
         return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
-                callingPackage, callingFeatureId, userId, false);
+                callingPackage, userId, false);
     }
 
     ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
             int callingPid, int callingUid, boolean fgRequired, String callingPackage,
-            @Nullable String callingFeatureId, final int userId,
-            boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
+            final int userId, boolean allowBackgroundActivityStarts)
+            throws TransactionTooLargeException {
         if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
                 + " type=" + resolvedType + " args=" + service.getExtras());
 
@@ -489,7 +488,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.getAppOpsManager().checkOpNoThrow(
+            final int mode = mAm.mAppOpsService.checkOperation(
                     AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
             switch (mode) {
                 case AppOpsManager.MODE_ALLOWED:
@@ -567,7 +566,7 @@
         // review is completed.
 
         // XXX This is not dealing with fgRequired!
-        if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage, callingFeatureId,
+        if (!requestStartTargetPermissionsReviewIfNeededLocked(r, callingPackage,
                 callingUid, service, callerFg, userId)) {
             return null;
         }
@@ -674,8 +673,8 @@
     }
 
     private boolean requestStartTargetPermissionsReviewIfNeededLocked(ServiceRecord r,
-            String callingPackage, @Nullable String callingFeatureId, int callingUid,
-            Intent service, boolean callerFg, final int userId) {
+            String callingPackage, int callingUid, Intent service, boolean callerFg,
+            final int userId) {
         if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                 r.packageName, r.userId)) {
 
@@ -687,7 +686,7 @@
             }
 
             IIntentSender target = mAm.mPendingIntentController.getIntentSender(
-                    ActivityManager.INTENT_SENDER_SERVICE, callingPackage, callingFeatureId,
+                    ActivityManager.INTENT_SENDER_SERVICE, callingPackage,
                     callingUid, userId, null, null, 0, new Intent[]{service},
                     new String[]{service.resolveType(mAm.mContext.getContentResolver())},
                     PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
@@ -1288,7 +1287,7 @@
             }
             // Instant apps need permission to create foreground services.
             if (r.appInfo.isInstantApp()) {
-                final int mode = mAm.getAppOpsManager().checkOpNoThrow(
+                final int mode = mAm.mAppOpsService.checkOperation(
                         AppOpsManager.OP_INSTANT_APP_START_FOREGROUND,
                         r.appInfo.uid,
                         r.appInfo.packageName);
@@ -1355,7 +1354,7 @@
 
             try {
                 boolean ignoreForeground = false;
-                final int mode = mAm.getAppOpsManager().checkOpNoThrow(
+                final int mode = mAm.mAppOpsService.checkOperation(
                         AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
                 switch (mode) {
                     case AppOpsManager.MODE_ALLOWED:
@@ -2290,7 +2289,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.getAppOpsManager().checkOpNoThrow(
+                if (opCode != AppOpsManager.OP_NONE && mAm.mAppOpsService.checkOperation(
                         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 d5a7253..b6dc3de 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -466,9 +466,18 @@
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real.
     static final int PROC_START_TIMEOUT = 10*1000;
+    // How long we wait for an attached process to publish its content providers
+    // before we decide it must be hung.
+    static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
+
     // How long we wait to kill an application zygote, after the last process using
     // it has gone away.
     static final int KILL_APP_ZYGOTE_DELAY_MS = 5 * 1000;
+    /**
+     * How long we wait for an provider to be published. Should be longer than
+     * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT}.
+     */
+    static final int CONTENT_PROVIDER_WAIT_TIMEOUT = 20 * 1000;
 
     // How long we wait for a launched process to attach to the activity manager
     // before we decide it's never going to come up for real, when the process was
@@ -1231,7 +1240,6 @@
      * 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.
@@ -2101,7 +2109,7 @@
                 new IAppOpsCallback.Stub() {
                     @Override public void opChanged(int op, int uid, String packageName) {
                         if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && packageName != null) {
-                            if (getAppOpsManager().checkOpNoThrow(op, uid, packageName)
+                            if (mAppOpsService.checkOperation(op, uid, packageName)
                                     != AppOpsManager.MODE_ALLOWED) {
                                 runInBackgroundDisabled(uid);
                             }
@@ -2392,13 +2400,6 @@
         }
     }
 
-    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.
@@ -2907,7 +2908,7 @@
     @Override
     public void batterySendBroadcast(Intent intent) {
         synchronized (this) {
-            broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
+            broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
                     OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(),
                     Binder.getCallingPid(), UserHandle.USER_ALL);
         }
@@ -3511,56 +3512,30 @@
 
     }
 
-    /**
-     * @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, null, intent,
-                resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);
+        return mActivityTaskManager.startActivity(caller, callingPackage, 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);
-    }
 
-    @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);
+            return mActivityTaskManager.startActivityAsUser(caller, callingPackage, intent,
+                    resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo,
+                    bOptions, userId);
     }
 
     WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
-            @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);
+            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);
     }
 
     @Override
@@ -4131,12 +4106,12 @@
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, resolvedUserId);
                     if (isInstantApp) {
                         intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
-                        broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
-                                null, null, 0, null, null, permission.ACCESS_INSTANT_APPS, null,
-                                false, false, resolvedUserId, false);
+                        broadcastIntentInPackage("android", SYSTEM_UID, uid, pid, intent, null,
+                                null, 0, null, null, permission.ACCESS_INSTANT_APPS, null, false,
+                                false, resolvedUserId, false);
                     } else {
-                        broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
-                                null, null, 0, null, null, null, null, false, false, resolvedUserId,
+                        broadcastIntentInPackage("android", SYSTEM_UID, uid, pid, intent, null,
+                                null, 0, null, null, null, null, false, false, resolvedUserId,
                                 false);
                     }
 
@@ -4584,7 +4559,7 @@
         }
         intent.putExtra(Intent.EXTRA_UID, uid);
         intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(uid));
-        broadcastIntentLocked(null, null, null, intent,
+        broadcastIntentLocked(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));
@@ -4959,8 +4934,7 @@
         if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
             Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
             msg.obj = app;
-            mHandler.sendMessageDelayed(msg,
-                    ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS);
+            mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
         }
 
         checkTime(startTime, "attachApplicationLocked: before bindApplication");
@@ -5442,23 +5416,12 @@
         }
     }
 
-    /**
-     * @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.
 
@@ -5520,13 +5483,12 @@
             }
 
             if (type == ActivityManager.INTENT_SENDER_ACTIVITY_RESULT) {
-                return mAtmInternal.getIntentSender(type, packageName, featureId, callingUid,
-                        userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
-                        bOptions);
+                return mAtmInternal.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);
+            return mPendingIntentController.getIntentSender(type, packageName, callingUid,
+                    userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
+                    bOptions);
         } catch (RemoteException e) {
             throw new SecurityException(e);
         }
@@ -6092,8 +6054,8 @@
             return ActivityManager.APP_START_MODE_DELAYED;
         }
         // Not in the RESTRICTED bucket so policy is based on AppOp check.
-        int appop = getAppOpsManager().noteOpNoThrow(AppOpsManager.OP_RUN_IN_BACKGROUND,
-                uid, packageName, null, "");
+        int appop = mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND,
+                uid, packageName, null, false, "");
         if (DEBUG_BACKGROUND_CHECK) {
             Slog.i(TAG, "Legacy app " + uid + "/" + packageName + " bg appop " + appop);
         }
@@ -7239,8 +7201,7 @@
         }
 
         // Wait for the provider to be published...
-        final long timeout =
-                SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS;
+        final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT;
         boolean timedOut = false;
         synchronized (cpr) {
             while (cpr.provider == null) {
@@ -7277,14 +7238,12 @@
             }
         }
         if (timedOut) {
-            // Note we do it after releasing the lock.
+            // Note we do it afer releasing the lock.
             String callerName = "unknown";
-            if (caller != null) {
-                synchronized (this) {
-                    final ProcessRecord record = mProcessList.getLRURecordForAppLocked(caller);
-                    if (record != null) {
-                        callerName = record.processName;
-                    }
+            synchronized (this) {
+                final ProcessRecord record = mProcessList.getLRURecordForAppLocked(caller);
+                if (record != null) {
+                    callerName = record.processName;
                 }
             }
 
@@ -8002,7 +7961,7 @@
     }
 
     boolean isBackgroundRestrictedNoCheck(final int uid, final String packageName) {
-        final int mode = getAppOpsManager().checkOpNoThrow(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+        final int mode = mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
                 uid, packageName);
         return mode != AppOpsManager.MODE_ALLOWED;
     }
@@ -9480,14 +9439,14 @@
                     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                             | Intent.FLAG_RECEIVER_FOREGROUND);
                     intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
-                    broadcastIntentLocked(null, null, null, intent,
+                    broadcastIntentLocked(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, null, intent, null,
+                    broadcastIntentLocked(null, null, intent, null,
                             new IIntentReceiver.Stub() {
                                 @Override
                                 public void performReceive(Intent intent, int resultCode,
@@ -11112,10 +11071,12 @@
         }
 
         if (dumpAll || dumpPackage != null) {
+            final SparseArray<ProcessRecord> pidToProcess = new SparseArray<>();
             synchronized (mPidsSelfLocked) {
                 boolean printed = false;
                 for (int i=0; i<mPidsSelfLocked.size(); i++) {
                     ProcessRecord r = mPidsSelfLocked.valueAt(i);
+                    pidToProcess.put(r.pid, r);
                     if (dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
                         continue;
                     }
@@ -11129,6 +11090,32 @@
                         pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i));
                 }
             }
+
+            synchronized (sActiveProcessInfoSelfLocked) {
+                boolean printed = false;
+                for (int i = 0; i < sActiveProcessInfoSelfLocked.size(); i++) {
+                    ProcessInfo info = sActiveProcessInfoSelfLocked.valueAt(i);
+                    ProcessRecord r = pidToProcess.get(sActiveProcessInfoSelfLocked.keyAt(i));
+                    if (r != null && dumpPackage != null && !r.pkgList.containsKey(dumpPackage)) {
+                        continue;
+                    }
+                    if (!printed) {
+                        if (needSep) pw.println();
+                        needSep = true;
+                        pw.println("  Active process infos:");
+                        printed = true;
+                    }
+                    pw.print("    Pinfo PID #"); pw.print(sActiveProcessInfoSelfLocked.keyAt(i));
+                    pw.println(":");
+                    pw.print("      name="); pw.println(info.name);
+                    if (info.deniedPermissions != null) {
+                        for (int j = 0; j < info.deniedPermissions.size(); j++) {
+                            pw.print("      deny: ");
+                            pw.println(info.deniedPermissions.valueAt(i));
+                        }
+                    }
+                }
+            }
         }
 
         if (mImportantProcesses.size() > 0) {
@@ -14626,8 +14613,7 @@
 
     @Override
     public ComponentName startService(IApplicationThread caller, Intent service,
-            String resolvedType, boolean requireForeground, String callingPackage,
-            String callingFeatureId, int userId)
+            String resolvedType, boolean requireForeground, String callingPackage, int userId)
             throws TransactionTooLargeException {
         enforceNotIsolatedCaller("startService");
         // Refuse possible leaked file descriptors
@@ -14649,7 +14635,7 @@
             try {
                 res = mServices.startServiceLocked(caller, service,
                         resolvedType, callingPid, callingUid,
-                        requireForeground, callingPackage, callingFeatureId, userId);
+                        requireForeground, callingPackage, userId);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -15127,20 +15113,9 @@
         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;
@@ -15276,7 +15251,7 @@
                         + " was previously registered for user " + rl.userId
                         + " callerPackage is " + callerPackage);
             }
-            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
+            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                     permission, callingUid, userId, instantApp, visibleToInstantApps);
             if (rl.containsFilter(filter)) {
                 Slog.w(TAG, "Receiver with filter " + filter
@@ -15301,7 +15276,7 @@
                     Intent intent = allSticky.get(i);
                     BroadcastQueue queue = broadcastQueueForIntent(intent);
                     BroadcastRecord r = new BroadcastRecord(queue, intent, null,
-                            null, null, -1, -1, false, null, null, OP_NONE, null, receivers,
+                            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);
@@ -15531,20 +15506,20 @@
 
     @GuardedBy("this")
     final int broadcastIntentLocked(ProcessRecord callerApp,
-            String callerPackage, String callerFeatureId, Intent intent, String resolvedType,
+            String callerPackage, 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, callerFeatureId, intent,
-                resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions,
-                appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
-                realCallingPid, userId, false /* allowBackgroundActivityStarts */);
+        return broadcastIntentLocked(callerApp, callerPackage, 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,
-            @Nullable String callerFeatureId, Intent intent, String resolvedType,
+    final int broadcastIntentLocked(ProcessRecord callerApp,
+            String callerPackage, 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,
@@ -16081,8 +16056,8 @@
                         isProtectedBroadcast, registeredReceivers);
             }
             final BroadcastQueue queue = broadcastQueueForIntent(intent);
-            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
-                    callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
+            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
+                    callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
                     resultCode, resultData, resultExtras, ordered, sticky, false, userId,
                     allowBackgroundActivityStarts, timeoutExempt);
@@ -16178,8 +16153,8 @@
         if ((receivers != null && receivers.size() > 0)
                 || resultTo != null) {
             BroadcastQueue queue = broadcastQueueForIntent(intent);
-            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
-                    callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
+            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
+                    callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                     resultData, resultExtras, ordered, sticky, false, userId,
                     allowBackgroundActivityStarts, timeoutExempt);
@@ -16298,25 +16273,11 @@
         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);
@@ -16328,7 +16289,7 @@
             final long origId = Binder.clearCallingIdentity();
             try {
                 return broadcastIntentLocked(callerApp,
-                        callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
+                        callerApp != null ? callerApp.info.packageName : null,
                         intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                         requiredPermissions, appOp, bOptions, serialized, sticky,
                         callingPid, callingUid, callingUid, callingPid, userId);
@@ -16338,9 +16299,9 @@
         }
     }
 
-    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,
+    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) {
         synchronized(this) {
@@ -16350,10 +16311,11 @@
             String[] requiredPermissions = requiredPermission == null ? null
                     : new String[] {requiredPermission};
             try {
-                return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
-                        resultTo, resultCode, resultData, resultExtras, requiredPermissions,
-                        OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid,
-                        realCallingPid, userId, allowBackgroundActivityStarts);
+                return broadcastIntentLocked(null, packageName, intent, resolvedType,
+                        resultTo, resultCode, resultData, resultExtras,
+                        requiredPermissions, OP_NONE, bOptions, serialized,
+                        sticky, -1, uid, realCallingUid, realCallingPid, userId,
+                        allowBackgroundActivityStarts);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -18957,24 +18919,23 @@
         }
 
         @Override
-        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) {
+        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) {
             synchronized (ActivityManagerService.this) {
-                return ActivityManagerService.this.broadcastIntentInPackage(packageName, featureId,
-                        uid, realCallingUid, realCallingPid, intent, resolvedType, resultTo,
-                        resultCode, resultData, resultExtras, requiredPermission, bOptions,
-                        serialized, sticky, userId, allowBackgroundActivityStarts);
+                return ActivityManagerService.this.broadcastIntentInPackage(packageName, 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, @Nullable String callingFeatureId,
-                int userId, boolean allowBackgroundActivityStarts)
-                throws TransactionTooLargeException {
+                boolean fgRequired, String callingPackage, int userId,
+                boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
             synchronized(ActivityManagerService.this) {
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                         "startServiceInPackage: " + service + " type=" + resolvedType);
@@ -18982,8 +18943,8 @@
                 ComponentName res;
                 try {
                     res = mServices.startServiceLocked(null, service,
-                            resolvedType, -1, uid, fgRequired, callingPackage,
-                            callingFeatureId, userId, allowBackgroundActivityStarts);
+                            resolvedType, -1, uid, fgRequired, callingPackage, userId,
+                            allowBackgroundActivityStarts);
                 } finally {
                     Binder.restoreCallingIdentity(origId);
                 }
@@ -19076,7 +19037,7 @@
                         | Intent.FLAG_RECEIVER_REPLACE_PENDING
                         | Intent.FLAG_RECEIVER_FOREGROUND
                         | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
-                broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
+                broadcastIntentLocked(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) {
@@ -19087,7 +19048,7 @@
                     if (initLocale || !mProcessesReady) {
                         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                     }
-                    broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
+                    broadcastIntentLocked(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);
                 }
@@ -19102,7 +19063,7 @@
                     // Typically only app stores will have this permission.
                     String[] permissions =
                             new String[] { android.Manifest.permission.INSTALL_PACKAGES };
-                    broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null,
+                    broadcastIntentLocked(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);
                 }
@@ -19127,7 +19088,7 @@
                     intent.putExtra("reason", reason);
                 }
 
-                broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
+                broadcastIntentLocked(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 c0e98cd..53a967b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -534,13 +534,13 @@
                 options.setLockTaskEnabled(true);
             }
             if (mWaitOption) {
-                result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, null, intent,
-                        mimeType, null, null, 0, mStartFlags, profilerInfo,
+                result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, intent, mimeType,
+                        null, null, 0, mStartFlags, profilerInfo,
                         options != null ? options.toBundle() : null, mUserId);
                 res = result.result;
             } else {
-                res = mInternal.startActivityAsUserWithFeature(null, SHELL_PACKAGE_NAME, null,
-                        intent, mimeType, null, null, 0, mStartFlags, profilerInfo,
+                res = mInternal.startActivityAsUser(null, SHELL_PACKAGE_NAME, intent, mimeType,
+                        null, null, 0, mStartFlags, profilerInfo,
                         options != null ? options.toBundle() : null, mUserId);
             }
             final long endTime = SystemClock.uptimeMillis();
@@ -652,7 +652,7 @@
         pw.println("Starting service: " + intent);
         pw.flush();
         ComponentName cn = mInterface.startService(null, intent, intent.getType(),
-                asForeground, SHELL_PACKAGE_NAME, null, mUserId);
+                asForeground, SHELL_PACKAGE_NAME, mUserId);
         if (cn == null) {
             err.println("Error: Not found; no service started.");
             return -1;
@@ -742,9 +742,8 @@
         pw.println("Broadcasting: " + intent);
         pw.flush();
         Bundle bundle = mBroadcastOptions == null ? null : mBroadcastOptions.toBundle();
-        mInterface.broadcastIntentWithFeature(null, null, intent, null, receiver, 0, null, null,
-                requiredPermissions, android.app.AppOpsManager.OP_NONE, bundle, true, false,
-                mUserId);
+        mInterface.broadcastIntent(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/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 145f91b..789f719 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -798,7 +798,6 @@
         boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
 
-        final String packageName;
         final int userId;
         synchronized (mService) {
             final ProcessRecord proc = data.proc;
@@ -807,7 +806,6 @@
                 Slog.e(TAG, "handleShowAppErrorUi: proc is null");
                 return;
             }
-            packageName = proc.info.packageName;
             userId = proc.userId;
             if (proc.getDialogController().hasCrashDialogs()) {
                 Slog.e(TAG, "App already has crash dialog: " + proc);
diff --git a/services/core/java/com/android/server/am/BroadcastFilter.java b/services/core/java/com/android/server/am/BroadcastFilter.java
index 578db6f..1ec8db0 100644
--- a/services/core/java/com/android/server/am/BroadcastFilter.java
+++ b/services/core/java/com/android/server/am/BroadcastFilter.java
@@ -27,7 +27,6 @@
     // 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;
@@ -35,12 +34,11 @@
     final boolean visibleToInstantApp;
 
     BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
-            String _packageName, String _featureId, String _requiredPermission, int _owningUid, int _userId,
+            String _packageName, 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 26ef707..6697b5a 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.getAppOpsManager().noteOpNoThrow(opCode, r.callingUid,
-                        r.callerPackage, r.callerFeatureId, "")
-                        != AppOpsManager.MODE_ALLOWED) {
+                        && mService.mAppOpsService.noteOperation(opCode, r.callingUid,
+                        r.callerPackage, null, false, "") != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: broadcasting "
                             + r.intent.toString()
                             + " from " + r.callerPackage + " (pid="
@@ -681,9 +681,10 @@
                     break;
                 }
                 int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
+                // TODO moltmann: Set featureId from caller
                 if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
-                        && mService.getAppOpsManager().noteOpNoThrow(appOp,
-                        filter.receiverList.uid, filter.packageName, filter.featureId, "")
+                        && mService.mAppOpsService.noteOperation(appOp,
+                        filter.receiverList.uid, filter.packageName, null, false, "")
                         != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: receiving "
                             + r.intent.toString()
@@ -713,9 +714,10 @@
                 skip = true;
             }
         }
+        // TODO moltmann: Set featureId from caller
         if (!skip && r.appOp != AppOpsManager.OP_NONE
-                && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
-                filter.receiverList.uid, filter.packageName, filter.featureId, "")
+                && mService.mAppOpsService.noteOperation(r.appOp,
+                filter.receiverList.uid, filter.packageName, null, false, "")
                 != AppOpsManager.MODE_ALLOWED) {
             Slog.w(TAG, "Appop Denial: receiving "
                     + r.intent.toString()
@@ -861,8 +863,7 @@
         if (callerForeground && receiverRecord.intent.getComponent() != null) {
             IIntentSender target = mService.mPendingIntentController.getIntentSender(
                     ActivityManager.INTENT_SENDER_BROADCAST, receiverRecord.callerPackage,
-                    receiverRecord.callerFeatureId, receiverRecord.callingUid,
-                    receiverRecord.userId, null, null, 0,
+                    receiverRecord.callingUid, receiverRecord.userId, null, null, 0,
                     new Intent[]{receiverRecord.intent},
                     new String[]{receiverRecord.intent.resolveType(mService.mContext
                             .getContentResolver())},
@@ -1370,9 +1371,10 @@
             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.getAppOpsManager().noteOpNoThrow(opCode, r.callingUid, r.callerPackage,
-                    r.callerFeatureId, "") != AppOpsManager.MODE_ALLOWED) {
+                    && mService.mAppOpsService.noteOperation(opCode, r.callingUid, r.callerPackage,
+                    null, false, "") != AppOpsManager.MODE_ALLOWED) {
                 Slog.w(TAG, "Appop Denial: broadcasting "
                         + r.intent.toString()
                         + " from " + r.callerPackage + " (pid="
@@ -1408,10 +1410,11 @@
                     break;
                 }
                 int appOp = AppOpsManager.permissionToOpCode(requiredPermission);
+                // TODO moltmann: Set featureId from caller
                 if (appOp != AppOpsManager.OP_NONE && appOp != r.appOp
-                        && mService.getAppOpsManager().noteOpNoThrow(appOp,
-                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
-                        null /* default featureId */, "")
+                        && mService.mAppOpsService.noteOperation(appOp,
+                        info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, null,
+                        false, "")
                         != AppOpsManager.MODE_ALLOWED) {
                     Slog.w(TAG, "Appop Denial: receiving "
                             + r.intent + " to "
@@ -1425,10 +1428,11 @@
                 }
             }
         }
+        // TODO moltmann: Set featureId from caller
         if (!skip && r.appOp != AppOpsManager.OP_NONE
-                && mService.getAppOpsManager().noteOpNoThrow(r.appOp,
-                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName,
-                null  /* default featureId */, "")
+                && mService.mAppOpsService.noteOperation(r.appOp,
+                info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, null, false,
+                "")
                 != 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 8ef67f9..f263886 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -16,7 +16,6 @@
 
 package com.android.server.am;
 
-import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.app.BroadcastOptions;
 import android.content.ComponentName;
@@ -52,7 +51,6 @@
     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?
@@ -235,8 +233,7 @@
 
     BroadcastRecord(BroadcastQueue _queue,
             Intent _intent, ProcessRecord _callerApp, String _callerPackage,
-            @Nullable String _callerFeatureId, int _callingPid, int _callingUid,
-            boolean _callerInstantApp, String _resolvedType,
+            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,
@@ -249,7 +246,6 @@
         targetComp = _intent.getComponent();
         callerApp = _callerApp;
         callerPackage = _callerPackage;
-        callerFeatureId = _callerFeatureId;
         callingPid = _callingPid;
         callingUid = _callingUid;
         callerInstantApp = _callerInstantApp;
@@ -284,7 +280,6 @@
 
         callerApp = from.callerApp;
         callerPackage = from.callerPackage;
-        callerFeatureId = from.callerFeatureId;
         callingPid = from.callingPid;
         callingUid = from.callingUid;
         callerInstantApp = from.callerInstantApp;
@@ -348,8 +343,8 @@
         }
 
         // build a new BroadcastRecord around that single-target list
-        BroadcastRecord split = new BroadcastRecord(queue, intent, callerApp, callerPackage,
-                callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
+        BroadcastRecord split = new BroadcastRecord(queue, intent, callerApp,
+                callerPackage, 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/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index d047a3c..eec68dc 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -946,6 +946,15 @@
                 }
 
                 EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
+
+                // See above for why we're not taking mPhenotypeFlagLock here
+                if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
+                    FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED,
+                            FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__FREEZE_APP,
+                            pid,
+                            name,
+                            unfrozenDuration);
+                }
             }
         }
 
@@ -994,6 +1003,16 @@
                 }
 
                 EventLog.writeEvent(EventLogTags.AM_UNFREEZE, pid, name);
+
+                // See above for why we're not taking mPhenotypeFlagLock here
+                if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
+                    FrameworkStatsLog.write(
+                            FrameworkStatsLog.APP_FREEZE_CHANGED,
+                            FrameworkStatsLog.APP_FREEZE_CHANGED__ACTION__UNFREEZE_APP,
+                            pid,
+                            name,
+                            frozenDuration);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index eacf088..df76713 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -23,7 +23,6 @@
 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;
@@ -89,9 +88,9 @@
         }
     }
 
-    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) {
+    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) {
         synchronized (mLock) {
             if (DEBUG_MU) Slog.v(TAG_MU, "getIntentSender(): uid=" + callingUid);
 
@@ -110,8 +109,8 @@
             flags &= ~(PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_CANCEL_CURRENT
                     | PendingIntent.FLAG_UPDATE_CURRENT);
 
-            PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, featureId,
-                    token, resultWho, requestCode, intents, resolvedTypes, flags,
+            PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, 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 d54d2d7..3ba2210 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -17,16 +17,14 @@
 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.app.PendingIntent;
-import android.content.IIntentReceiver;
 import android.content.IIntentSender;
+import android.content.IIntentReceiver;
+import android.app.PendingIntent;
 import android.content.Intent;
 import android.os.Binder;
 import android.os.Bundle;
@@ -74,7 +72,6 @@
     final static class Key {
         final int type;
         final String packageName;
-        final String featureId;
         final IBinder activity;
         final String who;
         final int requestCode;
@@ -89,11 +86,10 @@
 
         private static final int ODD_PRIME_NUMBER = 37;
 
-        Key(int _t, String _p, @Nullable String _featureId, IBinder _a, String _w,
+        Key(int _t, String _p, 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;
@@ -144,9 +140,6 @@
                 if (!Objects.equals(packageName, other.packageName)) {
                     return false;
                 }
-                if (!Objects.equals(featureId, other.featureId)) {
-                    return false;
-                }
                 if (activity != other.activity) {
                     return false;
                 }
@@ -182,8 +175,7 @@
         }
 
         public String toString() {
-            return "Key{" + typeName()
-                + " pkg=" + packageName + (featureId != null ? "/" + featureId : "")
+            return "Key{" + typeName() + " pkg=" + packageName
                 + " intent="
                 + (requestIntent != null
                         ? requestIntent.toShortString(false, true, false, false) : "<null>")
@@ -411,20 +403,19 @@
 
                         if (key.allIntents != null && key.allIntents.length > 1) {
                             res = controller.mAtmInternal.startActivitiesInPackage(
-                                    uid, callingPid, callingUid, key.packageName, key.featureId,
-                                    allIntents, allResolvedTypes, resultTo, mergedOptions, userId,
+                                    uid, callingPid, callingUid, key.packageName, allIntents,
+                                    allResolvedTypes, resultTo, mergedOptions, userId,
                                     false /* validateIncomingUser */,
                                     this /* originatingPendingIntent */,
                                     mAllowBgActivityStartsForActivitySender.contains(whitelistToken));
                         } else {
-                            res = controller.mAtmInternal.startActivityInPackage(uid, callingPid,
-                                    callingUid, key.packageName, key.featureId, finalIntent,
+                            res = controller.mAtmInternal.startActivityInPackage(
+                                    uid, callingPid, callingUid, key.packageName, 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);
@@ -439,12 +430,11 @@
                         // If a completion callback has been requested, require
                         // that the broadcast be delivered synchronously
                         int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName,
-                                key.featureId, uid, callingUid, callingPid, finalIntent,
-                                resolvedType, finishedReceiver, code, null, null,
-                                requiredPermission, options, (finishedReceiver != null), false,
-                                userId,
+                                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;
                         }
@@ -457,7 +447,7 @@
                     try {
                         controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType,
                                 key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
-                                key.packageName, key.featureId, userId,
+                                key.packageName, userId,
                                 mAllowBgActivityStartsForServiceSender.contains(whitelistToken)
                                 || allowTrampoline);
                     } catch (RuntimeException e) {
@@ -506,7 +496,6 @@
     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) {
@@ -556,10 +545,6 @@
         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 747e8a8..beb0e47 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, null, mIntent, null, this, 0, null, null,
-                    null, AppOpsManager.OP_NONE, null, true, false, ActivityManagerService.MY_PID,
+            mService.broadcastIntentLocked(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/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 2b4d15e..e63da9b 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -371,6 +371,15 @@
             }
         }
         pw.println("}");
+        if (processInfo != null) {
+            pw.print(prefix); pw.println("processInfo:");
+            if (processInfo.deniedPermissions != null) {
+                for (int i = 0; i < processInfo.deniedPermissions.size(); i++) {
+                    pw.print(prefix); pw.print("  deny: ");
+                    pw.println(processInfo.deniedPermissions.valueAt(i));
+                }
+            }
+        }
         pw.print(prefix); pw.print("mRequiredAbi="); pw.print(mRequiredAbi);
                 pw.print(" instructionSet="); pw.println(instructionSet);
         if (info.className != null) {
@@ -1901,8 +1910,7 @@
             mWaitDialog = null;
         }
 
-        void forAllDialogs(List<? extends BaseErrorDialog> dialogs,
-                Consumer<BaseErrorDialog> c) {
+        void forAllDialogs(List<? extends BaseErrorDialog> dialogs, Consumer<BaseErrorDialog> c) {
             for (int i = dialogs.size() - 1; i >= 0; i--) {
                 c.accept(dialogs.get(i));
             }
@@ -1911,42 +1919,72 @@
         void showCrashDialogs(AppErrorDialog.Data data) {
             List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */);
             mCrashDialogs = new ArrayList<>();
-
             for (int i = contexts.size() - 1; i >= 0; i--) {
                 final Context c = contexts.get(i);
                 mCrashDialogs.add(new AppErrorDialog(c, mService, data));
             }
-            mService.mUiHandler.post(() -> mCrashDialogs.forEach(Dialog::show));
+            mService.mUiHandler.post(() -> {
+                List<AppErrorDialog> dialogs;
+                synchronized (mService) {
+                    dialogs = mCrashDialogs;
+                }
+                if (dialogs != null) {
+                    forAllDialogs(dialogs, Dialog::show);
+                }
+            });
         }
 
         void showAnrDialogs(AppNotRespondingDialog.Data data) {
             List<Context> contexts = getDisplayContexts(isSilentAnr() /* lastUsedOnly */);
             mAnrDialogs = new ArrayList<>();
-
             for (int i = contexts.size() - 1; i >= 0; i--) {
                 final Context c = contexts.get(i);
                 mAnrDialogs.add(new AppNotRespondingDialog(mService, c, data));
             }
-            mService.mUiHandler.post(() -> mAnrDialogs.forEach(Dialog::show));
+            mService.mUiHandler.post(() -> {
+                List<AppNotRespondingDialog> dialogs;
+                synchronized (mService) {
+                    dialogs = mAnrDialogs;
+                }
+                if (dialogs != null) {
+                    forAllDialogs(dialogs, Dialog::show);
+                }
+            });
         }
 
         void showViolationDialogs(AppErrorResult res) {
             List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */);
             mViolationDialogs = new ArrayList<>();
-
             for (int i = contexts.size() - 1; i >= 0; i--) {
                 final Context c = contexts.get(i);
                 mViolationDialogs.add(
                         new StrictModeViolationDialog(c, mService, res, ProcessRecord.this));
             }
-            mService.mUiHandler.post(() -> mViolationDialogs.forEach(Dialog::show));
+            mService.mUiHandler.post(() -> {
+                List<StrictModeViolationDialog> dialogs;
+                synchronized (mService) {
+                    dialogs = mViolationDialogs;
+                }
+                if (dialogs != null) {
+                    forAllDialogs(dialogs, Dialog::show);
+                }
+            });
         }
 
         void showDebugWaitingDialogs() {
             List<Context> contexts = getDisplayContexts(true /* lastUsedOnly */);
             final Context c = contexts.get(0);
             mWaitDialog = new AppWaitingForDebuggerDialog(mService, c, ProcessRecord.this);
-            mService.mUiHandler.post(() -> mWaitDialog.show());
+
+            mService.mUiHandler.post(() -> {
+                Dialog dialog;
+                synchronized (mService) {
+                    dialog = mWaitDialog;
+                }
+                if (dialog != null) {
+                    dialog.show();
+                }
+            });
         }
 
         /**
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index e575e10..b2d0441 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -414,6 +414,7 @@
                 Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
                 intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
                 intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+                        | Intent.FLAG_RECEIVER_OFFLOAD
                         | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
                 mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
                         new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
@@ -2447,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, null, intent, resolvedType,
-                        resultTo, resultCode, resultData, resultExtras, requiredPermissions, appOp,
-                        bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
-                        realCallingPid, userId);
+                return mService.broadcastIntentLocked(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/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 1f998c3..ef1bc83 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -702,6 +702,10 @@
                 delay = 0;
             }
             mDeviceBroker.postSetHearingAidConnectionState(state, device, delay);
+            if (state == BluetoothHearingAid.STATE_CONNECTED) {
+                mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE,
+                                "HEARING_AID set to CONNECTED");
+            }
             return delay;
         }
     }
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 0cd0adb..26c94c5 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -25,6 +25,7 @@
 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
 import static android.hardware.biometrics.BiometricManager.Authenticators;
 
+import android.annotation.IntDef;
 import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.app.UserSwitchObserver;
@@ -83,6 +84,25 @@
     static final String TAG = "BiometricService";
     private static final boolean DEBUG = true;
 
+    private static final int BIOMETRIC_NO_HARDWARE = 0;
+    private static final int BIOMETRIC_OK = 1;
+    private static final int BIOMETRIC_DISABLED_BY_DEVICE_POLICY = 2;
+    private static final int BIOMETRIC_INSUFFICIENT_STRENGTH = 3;
+    private static final int BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE = 4;
+    private static final int BIOMETRIC_HARDWARE_NOT_DETECTED = 5;
+    private static final int BIOMETRIC_NOT_ENROLLED = 6;
+    private static final int BIOMETRIC_NOT_ENABLED_FOR_APPS = 7;
+
+    @IntDef({BIOMETRIC_NO_HARDWARE,
+            BIOMETRIC_OK,
+            BIOMETRIC_DISABLED_BY_DEVICE_POLICY,
+            BIOMETRIC_INSUFFICIENT_STRENGTH,
+            BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE,
+            BIOMETRIC_HARDWARE_NOT_DETECTED,
+            BIOMETRIC_NOT_ENROLLED,
+            BIOMETRIC_NOT_ENABLED_FOR_APPS})
+    @interface BiometricStatus {}
+
     private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2;
     private static final int MSG_ON_AUTHENTICATION_REJECTED = 3;
     private static final int MSG_ON_ERROR = 4;
@@ -206,7 +226,7 @@
         }
 
         boolean isAllowDeviceCredential() {
-            return Utils.isDeviceCredentialAllowed(mBundle);
+            return Utils.isCredentialRequested(mBundle);
         }
     }
 
@@ -372,16 +392,20 @@
          * strength.
          * @return a bitfield, see {@link Authenticators}
          */
-        public int getActualStrength() {
+        int getActualStrength() {
             return OEMStrength | updatedStrength;
         }
 
+        boolean isDowngraded() {
+            return OEMStrength != updatedStrength;
+        }
+
         /**
          * Stores the updated strength, which takes effect whenever {@link #getActualStrength()}
          * is checked.
          * @param newStrength
          */
-        public void updateStrength(int newStrength) {
+        void updateStrength(int newStrength) {
             String log = "updateStrength: Before(" + toString() + ")";
             updatedStrength = newStrength;
             log += " After(" + toString() + ")";
@@ -1007,6 +1031,79 @@
         return isBiometricDisabled;
     }
 
+    private static int biometricStatusToBiometricConstant(@BiometricStatus int status) {
+        switch (status) {
+            case BIOMETRIC_NO_HARDWARE:
+                return BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
+            case BIOMETRIC_OK:
+                return BiometricConstants.BIOMETRIC_SUCCESS;
+            case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
+                return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+            case BIOMETRIC_INSUFFICIENT_STRENGTH:
+                return BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
+            case BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE:
+                return BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
+            case BIOMETRIC_HARDWARE_NOT_DETECTED:
+                return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+            case BIOMETRIC_NOT_ENROLLED:
+                return BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS;
+            case BIOMETRIC_NOT_ENABLED_FOR_APPS:
+                return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+            default:
+                return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+        }
+    }
+
+    /**
+     * Returns the status of the authenticator, with errors returned in a specific priority order.
+     * For example, {@link #BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE} is only returned
+     * if it has enrollments, and is enabled for apps.
+     *
+     * We should only return the modality if the authenticator should be exposed. e.g.
+     * BIOMETRIC_NOT_ENROLLED_FOR_APPS should not expose the authenticator's type.
+     *
+     * @return A Pair with `first` being modality, and `second` being @BiometricStatus
+     */
+    private Pair<Integer, Integer> getStatusForBiometricAuthenticator(
+            AuthenticatorWrapper authenticator, int userId, String opPackageName,
+            boolean checkDevicePolicyManager, int requestedStrength) {
+        if (checkDevicePolicyManager) {
+            if (isBiometricDisabledByDevicePolicy(authenticator.modality, userId)) {
+                return new Pair<>(TYPE_NONE, BIOMETRIC_DISABLED_BY_DEVICE_POLICY);
+            }
+        }
+
+        final boolean wasStrongEnough =
+                Utils.isAtLeastStrength(authenticator.OEMStrength, requestedStrength);
+        final boolean isStrongEnough =
+                Utils.isAtLeastStrength(authenticator.getActualStrength(), requestedStrength);
+
+        if (wasStrongEnough && !isStrongEnough) {
+            return new Pair<>(authenticator.modality,
+                    BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE);
+        } else if (!wasStrongEnough) {
+            return new Pair<>(TYPE_NONE, BIOMETRIC_INSUFFICIENT_STRENGTH);
+        }
+
+        try {
+            if (!authenticator.impl.isHardwareDetected(opPackageName)) {
+                return new Pair<>(authenticator.modality, BIOMETRIC_HARDWARE_NOT_DETECTED);
+            }
+
+            if (!authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) {
+                return new Pair<>(authenticator.modality, BIOMETRIC_NOT_ENROLLED);
+            }
+        } catch (RemoteException e) {
+            return new Pair<>(authenticator.modality, BIOMETRIC_HARDWARE_NOT_DETECTED);
+        }
+
+        if (!isEnabledForApp(authenticator.modality, userId)) {
+            return new Pair<>(TYPE_NONE, BIOMETRIC_NOT_ENABLED_FOR_APPS);
+        }
+
+        return new Pair<>(authenticator.modality, BIOMETRIC_OK);
+    }
+
     /**
      * Depending on the requested authentication (credential/biometric combination), checks their
      * availability.
@@ -1029,10 +1126,9 @@
     private Pair<Integer, Integer> checkAndGetAuthenticators(int userId, Bundle bundle,
             String opPackageName, boolean checkDevicePolicyManager) throws RemoteException {
 
-        final boolean biometricRequested = Utils.isBiometricAllowed(bundle);
-        final boolean credentialRequested = Utils.isDeviceCredentialAllowed(bundle);
+        final boolean biometricRequested = Utils.isBiometricRequested(bundle);
+        final boolean credentialRequested = Utils.isCredentialRequested(bundle);
 
-        final boolean biometricOk;
         final boolean credentialOk = mTrustManager.isDeviceSecure(userId);
 
         // Assuming that biometric authenticators are listed in priority-order, the rest of this
@@ -1041,96 +1137,56 @@
         // the correct error. Error strings that are modality-specific should also respect the
         // priority-order.
 
-        // Find first biometric authenticator that's strong enough, detected, enrolled, and enabled.
-        boolean disabledByDevicePolicy = false;
-        boolean hasSufficientStrength = false;
-        boolean isHardwareDetected = false;
-        boolean hasTemplatesEnrolled = false;
-        boolean enabledForApps = false;
+        int firstBiometricModality = TYPE_NONE;
+        @BiometricStatus int firstBiometricStatus = BIOMETRIC_NO_HARDWARE;
 
-        int modality = TYPE_NONE;
-        int firstHwAvailable = TYPE_NONE;
+        int biometricModality = TYPE_NONE;
+        @BiometricStatus int biometricStatus = BIOMETRIC_NO_HARDWARE;
+
         for (AuthenticatorWrapper authenticator : mAuthenticators) {
-            final int actualStrength = authenticator.getActualStrength();
             final int requestedStrength = Utils.getPublicBiometricStrength(bundle);
+            Pair<Integer, Integer> result = getStatusForBiometricAuthenticator(
+                    authenticator, userId, opPackageName, checkDevicePolicyManager,
+                    requestedStrength);
 
-            if (isBiometricDisabledByDevicePolicy(authenticator.modality, userId)) {
-                disabledByDevicePolicy = true;
-                continue;
-            }
-            disabledByDevicePolicy = false;
+            biometricStatus = result.second;
 
-            if (!Utils.isAtLeastStrength(actualStrength, requestedStrength)) {
-                continue;
-            }
-            hasSufficientStrength = true;
+            Slog.d(TAG, "Authenticator ID: " + authenticator.id
+                    + " Modality: " + authenticator.modality
+                    + " ReportedModality: " + result.first
+                    + " Status: " + biometricStatus);
 
-            if (!authenticator.impl.isHardwareDetected(opPackageName)) {
-                continue;
-            }
-            isHardwareDetected = true;
-
-            if (firstHwAvailable == TYPE_NONE) {
-                // Store the first one since we want to return the error in correct
-                // priority order.
-                firstHwAvailable = authenticator.modality;
+            if (firstBiometricModality == TYPE_NONE) {
+                firstBiometricModality = result.first;
+                firstBiometricStatus = biometricStatus;
             }
 
-            if (!authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) {
-                continue;
+            if (biometricStatus == BIOMETRIC_OK) {
+                biometricModality = result.first;
+                break;
             }
-            hasTemplatesEnrolled = true;
-
-            if (!isEnabledForApp(authenticator.modality, userId)) {
-                continue;
-            }
-            enabledForApps = true;
-            modality = authenticator.modality;
-            break;
         }
 
-        biometricOk = !disabledByDevicePolicy
-                && hasSufficientStrength && isHardwareDetected
-                && hasTemplatesEnrolled && enabledForApps;
-
-        Slog.d(TAG, "checkAndGetAuthenticators: user=" + userId
-                + " checkDevicePolicyManager=" + checkDevicePolicyManager
-                + " isHardwareDetected=" + isHardwareDetected
-                + " hasTemplatesEnrolled=" + hasTemplatesEnrolled
-                + " enabledForApps=" + enabledForApps
-                + " disabledByDevicePolicy=" + disabledByDevicePolicy);
-
         if (biometricRequested && credentialRequested) {
-            if (credentialOk || biometricOk) {
-                if (!biometricOk) {
+            if (credentialOk || biometricStatus == BIOMETRIC_OK) {
+                if (biometricStatus != BIOMETRIC_OK) {
                     // If there's a problem with biometrics but device credential is
                     // allowed, only show credential UI.
                     bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
                             Authenticators.DEVICE_CREDENTIAL);
                 }
-                return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
+                return new Pair<>(biometricModality, BiometricConstants.BIOMETRIC_SUCCESS);
             } else {
-                return new Pair<>(firstHwAvailable,
+                return new Pair<>(firstBiometricModality,
                         BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
             }
         } else if (biometricRequested) {
-            if (biometricOk) {
-                return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
-            } else if (disabledByDevicePolicy) {
-                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
-            } else if (!hasSufficientStrength) {
-                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
-            } else if (!isHardwareDetected) {
-                return new Pair<>(firstHwAvailable,
-                        BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
-            } else if (!hasTemplatesEnrolled) {
-                return new Pair<>(firstHwAvailable,
-                        BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
-            } else if (!enabledForApps) {
-                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+            if (biometricStatus == BIOMETRIC_OK) {
+                return new Pair<>(biometricModality,
+                        biometricStatusToBiometricConstant(biometricStatus));
             } else {
-                Slog.e(TAG, "Unexpected case");
-                return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+                return new Pair<>(firstBiometricModality,
+                        biometricStatusToBiometricConstant(firstBiometricStatus));
             }
         } else if (credentialRequested) {
             if (credentialOk) {
diff --git a/services/core/java/com/android/server/biometrics/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/LoggableMonitor.java
index d101586..c50ab17 100644
--- a/services/core/java/com/android/server/biometrics/LoggableMonitor.java
+++ b/services/core/java/com/android/server/biometrics/LoggableMonitor.java
@@ -129,7 +129,7 @@
                 error,
                 vendorCode,
                 Utils.isDebugEnabled(context, targetUserId),
-                latency);
+                sanitizeLatency(latency));
     }
 
     protected final void logOnAuthenticated(Context context, boolean authenticated,
@@ -170,7 +170,7 @@
                 statsClient(),
                 requireConfirmation,
                 authState,
-                latency,
+                sanitizeLatency(latency),
                 Utils.isDebugEnabled(context, targetUserId));
     }
 
@@ -188,8 +188,16 @@
         FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
                 statsModality(),
                 targetUserId,
-                latency,
+                sanitizeLatency(latency),
                 enrollSuccessful);
     }
 
+    private long sanitizeLatency(long latency) {
+        if (latency < 0) {
+            Slog.w(TAG, "found a negative latency : " + latency);
+            return -1;
+        }
+        return latency;
+    }
+
 }
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 2d4ab63..8f3fd36 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -80,7 +80,7 @@
      * @param authenticators composed of one or more values from {@link Authenticators}
      * @return true if device credential is allowed.
      */
-    public static boolean isDeviceCredentialAllowed(@Authenticators.Types int authenticators) {
+    public static boolean isCredentialRequested(@Authenticators.Types int authenticators) {
         return (authenticators & Authenticators.DEVICE_CREDENTIAL) != 0;
     }
 
@@ -88,8 +88,8 @@
      * @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
      * @return true if device credential is allowed.
      */
-    public static boolean isDeviceCredentialAllowed(Bundle bundle) {
-        return isDeviceCredentialAllowed(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+    public static boolean isCredentialRequested(Bundle bundle) {
+        return isCredentialRequested(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
     }
 
     /**
@@ -120,7 +120,7 @@
      * @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
      * @return true if biometric authentication is allowed.
      */
-    public static boolean isBiometricAllowed(Bundle bundle) {
+    public static boolean isBiometricRequested(Bundle bundle) {
         return getPublicBiometricStrength(bundle) != 0;
     }
 
@@ -169,7 +169,7 @@
         // should be set.
         final int biometricBits = authenticators & Authenticators.BIOMETRIC_MIN_STRENGTH;
         if (biometricBits == Authenticators.EMPTY_SET
-                && isDeviceCredentialAllowed(authenticators)) {
+                && isCredentialRequested(authenticators)) {
             return true;
         } else if (biometricBits == Authenticators.BIOMETRIC_STRONG) {
             return true;
@@ -209,6 +209,9 @@
             case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
                 biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
                 break;
+            case BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
+                biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
+                break;
             default:
                 Slog.e(BiometricService.TAG, "Unhandled result code: " + biometricConstantsCode);
                 biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
diff --git a/services/core/java/com/android/server/compat/TEST_MAPPING b/services/core/java/com/android/server/compat/TEST_MAPPING
new file mode 100644
index 0000000..0c30c79
--- /dev/null
+++ b/services/core/java/com/android/server/compat/TEST_MAPPING
@@ -0,0 +1,21 @@
+{
+    "presubmit": [
+        // Unit tests
+        {
+            "name": "FrameworksServicesTests",
+            "options": [
+                {
+                    "include-filter": "com.android.server.compat"
+                }
+            ]
+        },
+        // Tests for the TestRule
+        {
+            "name": "PlatformCompatGating"
+        },
+        // CTS tests
+        {
+            "name": "CtsAppCompatHostTestCases#"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
index b0e2e64..6a5e963 100644
--- a/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
+++ b/services/core/java/com/android/server/incremental/IncrementalManagerShellCommand.java
@@ -16,9 +16,7 @@
 
 package com.android.server.incremental;
 
-import static android.content.pm.InstallationFile.FILE_TYPE_OBB;
 import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
-import static android.content.pm.PackageInstaller.LOCATION_MEDIA_OBB;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -183,10 +181,8 @@
             session = packageInstaller.openSession(sessionId);
             for (int i = 0; i < numFiles; i++) {
                 InstallationFile file = installationFiles.get(i);
-                final int location = file.getFileType() == FILE_TYPE_OBB ? LOCATION_MEDIA_OBB
-                        : LOCATION_DATA_APP;
-                session.addFile(location, file.getName(), file.getSize(), file.getMetadata(),
-                        null);
+                session.addFile(file.getLocation(), file.getName(), file.getLengthBytes(),
+                        file.getMetadata(), file.getSignature());
             }
             session.commit(localReceiver.getIntentSender());
             final Intent result = localReceiver.getResult();
@@ -304,7 +300,8 @@
                     }
                     final byte[] metadata = String.valueOf(index).getBytes(
                             StandardCharsets.UTF_8);
-                    fileList.add(new InstallationFile(name, size, metadata));
+                    fileList.add(
+                            new InstallationFile(LOCATION_DATA_APP, name, size, metadata, null));
                     break;
                 }
                 default:
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
index 0450647..68ced79 100644
--- a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -272,21 +272,15 @@
             List<String> installerCertificates =
                     getInstallerCertificateFingerprint(installerPackageName);
 
-            // TODO (b/148373316): Figure out what field contains which fields are populated for
-            // rotated and the multiple signers. Until then, return the first certificate.
-            String appCert = appCertificates.isEmpty() ? "" : appCertificates.get(0);
-            String installerCert =
-                    installerCertificates.isEmpty() ? "" : installerCertificates.get(0);
-
             Slog.w(TAG, appCertificates.toString());
 
             AppInstallMetadata.Builder builder = new AppInstallMetadata.Builder();
 
             builder.setPackageName(getPackageNameNormalized(packageName));
-            builder.setAppCertificate(appCert);
+            builder.setAppCertificates(appCertificates);
             builder.setVersionCode(intent.getLongExtra(EXTRA_LONG_VERSION_CODE, -1));
             builder.setInstallerName(getPackageNameNormalized(installerPackageName));
-            builder.setInstallerCertificate(installerCert);
+            builder.setInstallerCertificates(installerCertificates);
             builder.setIsPreInstalled(isSystemApp(packageName));
 
             AppInstallMetadata appInstallMetadata = builder.build();
@@ -307,7 +301,7 @@
             FrameworkStatsLog.write(
                     FrameworkStatsLog.INTEGRITY_CHECK_RESULT_REPORTED,
                     packageName,
-                    appCert,
+                    appCertificates.toString(),
                     appInstallMetadata.getVersionCode(),
                     installerPackageName,
                     result.getLoggingResponse(),
diff --git a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
index 87eee4e..60e8cce 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleIndexingController.java
@@ -63,10 +63,12 @@
                 searchIndexingKeysRangeContainingKey(
                         sPackageNameBasedIndexes, appInstallMetadata.getPackageName()));
 
-        // Add the range for app certificate indexes rules.
-        indexRanges.add(
-                searchIndexingKeysRangeContainingKey(
-                        sAppCertificateBasedIndexes, appInstallMetadata.getAppCertificate()));
+        // Add the range for app certificate indexes rules of all certificates.
+        for (String appCertificate : appInstallMetadata.getAppCertificates()) {
+            indexRanges.add(
+                    searchIndexingKeysRangeContainingKey(
+                            sAppCertificateBasedIndexes, appCertificate));
+        }
 
         // Add the range for unindexed rules.
         indexRanges.add(
diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
index 55e427f..6ba5f07 100644
--- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.location.GnssMeasurementsEvent;
+import android.location.GnssRequest;
 import android.location.IGnssMeasurementsListener;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -33,14 +34,14 @@
  * @hide
  */
 public abstract class GnssMeasurementsProvider
-        extends RemoteListenerHelper<IGnssMeasurementsListener> {
-    private static final String TAG = "GnssMeasurementsProvider";
+        extends RemoteListenerHelper<GnssRequest, IGnssMeasurementsListener> {
+    private static final String TAG = "GnssMeasProvider";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final GnssMeasurementProviderNative mNative;
 
-    private boolean mIsCollectionStarted;
-    private boolean mEnableFullTracking;
+    private boolean mStartedCollection;
+    private boolean mStartedFullTracking;
 
     protected GnssMeasurementsProvider(Context context, Handler handler) {
         this(context, handler, new GnssMeasurementProviderNative());
@@ -57,8 +58,8 @@
         if (DEBUG) {
             Log.d(TAG, "resumeIfStarted");
         }
-        if (mIsCollectionStarted) {
-            mNative.startMeasurementCollection(mEnableFullTracking);
+        if (mStartedCollection) {
+            mNative.startMeasurementCollection(mStartedFullTracking);
         }
     }
 
@@ -67,18 +68,35 @@
         return mNative.isMeasurementSupported();
     }
 
-    @Override
-    protected int registerWithService() {
+    private boolean getMergedFullTracking() {
         int devOptions = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
-        int fullTrackingToggled = Settings.Global.getInt(mContext.getContentResolver(),
+        int enableFullTracking = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, 0);
-        boolean enableFullTracking = (devOptions == 1 /* Developer Mode enabled */)
-                && (fullTrackingToggled == 1 /* Raw Measurements Full Tracking enabled */);
+        boolean enableFullTrackingBySetting = (devOptions == 1 /* Developer Mode enabled */)
+                && (enableFullTracking == 1 /* Raw Measurements Full Tracking enabled */);
+        if (enableFullTrackingBySetting) {
+            return true;
+        }
+
+        synchronized (mListenerMap) {
+            for (IdentifiedListener identifiedListener : mListenerMap.values()) {
+                GnssRequest request = identifiedListener.getRequest();
+                if (request != null && request.isFullTracking()) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    protected int registerWithService() {
+        boolean enableFullTracking = getMergedFullTracking();
         boolean result = mNative.startMeasurementCollection(enableFullTracking);
         if (result) {
-            mIsCollectionStarted = true;
-            mEnableFullTracking = enableFullTracking;
+            mStartedCollection = true;
+            mStartedFullTracking = enableFullTracking;
             return RemoteListenerHelper.RESULT_SUCCESS;
         } else {
             return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
@@ -89,7 +107,7 @@
     protected void unregisterFromService() {
         boolean stopped = mNative.stopMeasurementCollection();
         if (stopped) {
-            mIsCollectionStarted = false;
+            mStartedCollection = false;
         }
     }
 
diff --git a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
index 983d1da..fb901e8 100644
--- a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
@@ -33,7 +33,7 @@
  * @hide
  */
 public abstract class GnssNavigationMessageProvider
-        extends RemoteListenerHelper<IGnssNavigationMessageListener> {
+        extends RemoteListenerHelper<Void, IGnssNavigationMessageListener> {
     private static final String TAG = "GnssNavigationMessageProvider";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
diff --git a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
index eaf63c8..1d16c03 100644
--- a/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GnssStatusListenerHelper.java
@@ -24,7 +24,8 @@
 /**
  * Implementation of a handler for {@link IGnssStatusListener}.
  */
-public abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatusListener> {
+public abstract class GnssStatusListenerHelper extends
+        RemoteListenerHelper<Void, IGnssStatusListener> {
     private static final String TAG = "GnssStatusListenerHelper";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index 01522739..11f0685 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -17,6 +17,7 @@
 package com.android.server.location;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.os.Handler;
@@ -32,9 +33,10 @@
 /**
  * A helper class that handles operations in remote listeners.
  *
+ * @param <TRequest> the type of request.
  * @param <TListener> the type of GNSS data listener.
  */
-public abstract class RemoteListenerHelper<TListener extends IInterface> {
+public abstract class RemoteListenerHelper<TRequest, TListener extends IInterface> {
 
     protected static final int RESULT_SUCCESS = 0;
     protected static final int RESULT_NOT_AVAILABLE = 1;
@@ -47,7 +49,7 @@
     protected final Handler mHandler;
     private final String mTag;
 
-    private final Map<IBinder, IdentifiedListener> mListenerMap = new HashMap<>();
+    protected final Map<IBinder, IdentifiedListener> mListenerMap = new HashMap<>();
 
     protected final Context mContext;
     protected final AppOpsManager mAppOps;
@@ -75,7 +77,8 @@
     /**
      * Adds GNSS data listener {@code listener} with caller identify {@code callerIdentify}.
      */
-    public void addListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
+    public void addListener(@Nullable TRequest request, @NonNull TListener listener,
+            CallerIdentity callerIdentity) {
         Objects.requireNonNull(listener, "Attempted to register a 'null' listener.");
         IBinder binder = listener.asBinder();
         synchronized (mListenerMap) {
@@ -84,7 +87,7 @@
                 return;
             }
 
-            IdentifiedListener identifiedListener = new IdentifiedListener(listener,
+            IdentifiedListener identifiedListener = new IdentifiedListener(request, listener,
                     callerIdentity);
             mListenerMap.put(binder, identifiedListener);
 
@@ -257,14 +260,22 @@
         return RESULT_SUCCESS;
     }
 
-    private class IdentifiedListener {
+    protected class IdentifiedListener {
+        @Nullable private final TRequest mRequest;
         private final TListener mListener;
         private final CallerIdentity mCallerIdentity;
 
-        private IdentifiedListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
+        private IdentifiedListener(@Nullable TRequest request, @NonNull TListener listener,
+                CallerIdentity callerIdentity) {
             mListener = listener;
+            mRequest = request;
             mCallerIdentity = callerIdentity;
         }
+
+        @Nullable
+        protected TRequest getRequest() {
+            return mRequest;
+        }
     }
 
     private class HandlerRunnable implements Runnable {
diff --git a/services/core/java/com/android/server/location/SettingsHelper.java b/services/core/java/com/android/server/location/SettingsHelper.java
index 9163490..6d1d1f9 100644
--- a/services/core/java/com/android/server/location/SettingsHelper.java
+++ b/services/core/java/com/android/server/location/SettingsHelper.java
@@ -135,6 +135,24 @@
     }
 
     /**
+     * Set location enabled for a user.
+     */
+    public void setLocationEnabled(boolean enabled, int userId) {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            Settings.Secure.putIntForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.LOCATION_MODE,
+                    enabled
+                        ? Settings.Secure.LOCATION_MODE_ON
+                        : Settings.Secure.LOCATION_MODE_OFF,
+                    userId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
      * Add a listener for changes to the location enabled setting. Callbacks occur on an unspecified
      * thread.
      */
diff --git a/services/core/java/com/android/server/location/TEST_MAPPING b/services/core/java/com/android/server/location/TEST_MAPPING
index 2e21fa6..214d2f3 100644
--- a/services/core/java/com/android/server/location/TEST_MAPPING
+++ b/services/core/java/com/android/server/location/TEST_MAPPING
@@ -1,10 +1,19 @@
 {
   "presubmit": [
     {
+      "name": "CtsLocationFineTestCases"
+    },
+    {
       "name": "CtsLocationCoarseTestCases"
     },
     {
       "name": "CtsLocationNoneTestCases"
+    },
+    {
+      "name": "FrameworksMockingServicesTests",
+      "options": [{
+        "include-filter": "com.android.server.location"
+      }]
     }
   ]
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 2bab9fa..02c1bdd 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.location.GnssCapabilities;
 import android.location.GnssMeasurementCorrections;
+import android.location.GnssRequest;
 import android.location.IBatchedLocationCallback;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
@@ -96,15 +97,15 @@
     private final IGpsGeofenceHardware mGpsGeofenceProxy;
 
     @GuardedBy("mGnssMeasurementsListeners")
-    private final ArrayMap<IBinder, LinkedListener<IGnssMeasurementsListener>>
+    private final ArrayMap<IBinder, LinkedListener<GnssRequest, IGnssMeasurementsListener>>
             mGnssMeasurementsListeners = new ArrayMap<>();
 
     @GuardedBy("mGnssNavigationMessageListeners")
-    private final ArrayMap<IBinder, LinkedListener<IGnssNavigationMessageListener>>
+    private final ArrayMap<IBinder, LinkedListener<Void, IGnssNavigationMessageListener>>
             mGnssNavigationMessageListeners = new ArrayMap<>();
 
     @GuardedBy("mGnssStatusListeners")
-    private final ArrayMap<IBinder, LinkedListener<IGnssStatusListener>>
+    private final ArrayMap<IBinder, LinkedListener<Void, IGnssStatusListener>>
             mGnssStatusListeners = new ArrayMap<>();
 
     @GuardedBy("this")
@@ -118,7 +119,8 @@
     @Nullable private IBatchedLocationCallback mGnssBatchingCallback;
 
     @GuardedBy("mGnssBatchingLock")
-    @Nullable private LinkedListener<IBatchedLocationCallback> mGnssBatchingDeathCallback;
+    @Nullable
+    private LinkedListener<Void, IBatchedLocationCallback> mGnssBatchingDeathCallback;
 
     @GuardedBy("mGnssBatchingLock")
     private boolean mGnssBatchingInProgress = false;
@@ -272,6 +274,7 @@
             mGnssBatchingCallback = callback;
             mGnssBatchingDeathCallback =
                     new LinkedListener<>(
+                            /* request= */ null,
                             callback,
                             "BatchedLocationCallback",
                             callerIdentity,
@@ -356,36 +359,39 @@
         }
     }
 
-    private <TListener extends IInterface> void updateListenersOnForegroundChangedLocked(
-            Map<IBinder, ? extends LinkedListenerBase> gnssDataListeners,
-            RemoteListenerHelper<TListener> gnssDataProvider,
+    private <TRequest, TListener extends IInterface> void updateListenersOnForegroundChangedLocked(
+            Map<IBinder, LinkedListener<TRequest, TListener>> gnssDataListeners,
+            RemoteListenerHelper<TRequest, TListener> gnssDataProvider,
             Function<IBinder, TListener> mapBinderToListener,
             int uid,
             boolean foreground) {
-        for (Map.Entry<IBinder, ? extends LinkedListenerBase> entry :
+        for (Map.Entry<IBinder, LinkedListener<TRequest, TListener>> entry :
                 gnssDataListeners.entrySet()) {
-            LinkedListenerBase linkedListener = entry.getValue();
+            LinkedListener<TRequest, TListener> linkedListener = entry.getValue();
             CallerIdentity callerIdentity = linkedListener.getCallerIdentity();
+            TRequest request = linkedListener.getRequest();
             if (callerIdentity.mUid != uid) {
                 continue;
             }
 
             TListener listener = mapBinderToListener.apply(entry.getKey());
             if (foreground || isThrottlingExempt(callerIdentity)) {
-                gnssDataProvider.addListener(listener, callerIdentity);
+                gnssDataProvider.addListener(request, listener, callerIdentity);
             } else {
                 gnssDataProvider.removeListener(listener);
             }
         }
     }
 
-    private <TListener extends IInterface> boolean addGnssDataListenerLocked(
+    private <TListener extends IInterface, TRequest> boolean addGnssDataListenerLocked(
+            @Nullable TRequest request,
             TListener listener,
             String packageName,
             @Nullable String featureId,
             @NonNull String listenerIdentifier,
-            RemoteListenerHelper<TListener> gnssDataProvider,
-            ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners,
+            RemoteListenerHelper<TRequest, TListener> gnssDataProvider,
+            ArrayMap<IBinder,
+                    LinkedListener<TRequest, TListener>> gnssDataListeners,
             Consumer<TListener> binderDeathCallback) {
         mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
 
@@ -395,7 +401,7 @@
 
         CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
                 Binder.getCallingPid(), packageName, featureId, listenerIdentifier);
-        LinkedListener<TListener> linkedListener = new LinkedListener<>(listener,
+        LinkedListener<TRequest, TListener> linkedListener = new LinkedListener<>(request, listener,
                 listenerIdentifier, callerIdentity, binderDeathCallback);
         IBinder binder = listener.asBinder();
         if (!linkedListener.linkToListenerDeathNotificationLocked(binder)) {
@@ -419,15 +425,15 @@
         }
         if (mAppForegroundHelper.isAppForeground(callerIdentity.mUid)
                 || isThrottlingExempt(callerIdentity)) {
-            gnssDataProvider.addListener(listener, callerIdentity);
+            gnssDataProvider.addListener(request, listener, callerIdentity);
         }
         return true;
     }
 
-    private <TListener extends IInterface> void removeGnssDataListenerLocked(
+    private <TRequest, TListener extends IInterface> void removeGnssDataListenerLocked(
             TListener listener,
-            RemoteListenerHelper<TListener> gnssDataProvider,
-            ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners) {
+            RemoteListenerHelper<TRequest, TListener> gnssDataProvider,
+            ArrayMap<IBinder, LinkedListener<TRequest, TListener>> gnssDataListeners) {
         if (gnssDataProvider == null) {
             Log.e(
                     TAG,
@@ -437,7 +443,7 @@
         }
 
         IBinder binder = listener.asBinder();
-        LinkedListener<TListener> linkedListener =
+        LinkedListener<TRequest, TListener> linkedListener =
                 gnssDataListeners.remove(binder);
         if (linkedListener == null) {
             return;
@@ -467,6 +473,7 @@
             @Nullable String featureId) {
         synchronized (mGnssStatusListeners) {
             return addGnssDataListenerLocked(
+                    /* request= */ null,
                     listener,
                     packageName,
                     featureId,
@@ -489,11 +496,17 @@
     /**
      * Adds a GNSS measurements listener.
      */
-    public boolean addGnssMeasurementsListener(
-            IGnssMeasurementsListener listener, String packageName, @Nullable String featureId,
+    public boolean addGnssMeasurementsListener(@Nullable GnssRequest request,
+            IGnssMeasurementsListener listener, String packageName,
+            @Nullable String featureId,
             @NonNull String listenerIdentifier) {
+        if (request != null && request.isFullTracking()) {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.LOCATION_HARDWARE,
+                    null);
+        }
         synchronized (mGnssMeasurementsListeners) {
             return addGnssDataListenerLocked(
+                    request,
                     listener,
                     packageName,
                     featureId,
@@ -538,6 +551,7 @@
             @Nullable String featureId, @NonNull String listenerIdentifier) {
         synchronized (mGnssNavigationMessageListeners) {
             return addGnssDataListenerLocked(
+                    /* request= */ null,
                     listener,
                     packageName,
                     featureId,
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
new file mode 100644
index 0000000..58c2707
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright 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.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ComponentInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Holds the media button receiver, and also provides helper methods around it.
+ */
+final class MediaButtonReceiverHolder {
+    public static final int COMPONENT_TYPE_INVALID = 0;
+    public static final int COMPONENT_TYPE_BROADCAST = 1;
+    public static final int COMPONENT_TYPE_ACTIVITY = 2;
+    public static final int COMPONENT_TYPE_SERVICE = 3;
+
+    @IntDef(value = {
+            COMPONENT_TYPE_INVALID,
+            COMPONENT_TYPE_BROADCAST,
+            COMPONENT_TYPE_ACTIVITY,
+            COMPONENT_TYPE_SERVICE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ComponentType {}
+
+    private static final String TAG = "PendingIntentHolder";
+    private static final boolean DEBUG_KEY_EVENT = MediaSessionService.DEBUG_KEY_EVENT;
+    private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
+
+    private final int mUserId;
+    private final PendingIntent mPendingIntent;
+    private final ComponentName mComponentName;
+    private final String mPackageName;
+    @ComponentType
+    private final int mComponentType;
+
+    /**
+     * Unflatten from string which is previously flattened string via flattenToString().
+     * <p>
+     * It's used to store and restore media button receiver across the boot, by keeping the intent's
+     * component name to the persistent storage.
+     *
+     * @param mediaButtonReceiverInfo previously flattened string via flattenToString()
+     * @return new instance if the string was valid. {@code null} otherwise.
+     */
+    public static MediaButtonReceiverHolder unflattenFromString(
+            Context context, String mediaButtonReceiverInfo) {
+        if (TextUtils.isEmpty(mediaButtonReceiverInfo)) {
+            return null;
+        }
+        String[] tokens = mediaButtonReceiverInfo.split(COMPONENT_NAME_USER_ID_DELIM);
+        if (tokens == null || (tokens.length != 2 && tokens.length != 3)) {
+            return null;
+        }
+        ComponentName componentName = ComponentName.unflattenFromString(tokens[0]);
+        int userId = Integer.parseInt(tokens[1]);
+        // Guess component type if the OS version is updated from the older version.
+        int componentType = (tokens.length == 3)
+                ?  Integer.parseInt(tokens[2])
+                : getComponentType(context, componentName);
+        return new MediaButtonReceiverHolder(userId, null, componentName, componentType);
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param context context
+     * @param userId userId
+     * @param pendingIntent pending intent
+     * @return Can be {@code null} if pending intent was null.
+     */
+    public static MediaButtonReceiverHolder create(Context context, int userId,
+            PendingIntent pendingIntent) {
+        if (pendingIntent == null) {
+            return null;
+        }
+        ComponentName componentName = (pendingIntent != null && pendingIntent.getIntent() != null)
+                ? pendingIntent.getIntent().getComponent() : null;
+        if (componentName != null) {
+            // Explicit intent, where component name is in the PendingIntent.
+            return new MediaButtonReceiverHolder(userId, pendingIntent, componentName,
+                    getComponentType(context, componentName));
+        }
+
+        // Implicit intent, where component name isn't in the PendingIntent. Try resolve.
+        PackageManager pm = context.getPackageManager();
+        Intent intent = pendingIntent.getIntent();
+        if ((componentName = resolveImplicitServiceIntent(pm, intent)) != null) {
+            return new MediaButtonReceiverHolder(
+                    userId, pendingIntent, componentName, COMPONENT_TYPE_SERVICE);
+        } else if ((componentName = resolveManifestDeclaredBroadcastReceiverIntent(pm, intent))
+                != null) {
+            return new MediaButtonReceiverHolder(
+                    userId, pendingIntent, componentName, COMPONENT_TYPE_BROADCAST);
+        } else if ((componentName = resolveImplicitActivityIntent(pm, intent)) != null) {
+            return new MediaButtonReceiverHolder(
+                    userId, pendingIntent, componentName, COMPONENT_TYPE_ACTIVITY);
+        }
+
+        // Failed to resolve target component for the pending intent. It's unlikely to be usable.
+        // However, the pending intent would be still used, just to follow the legacy behavior.
+        Log.w(TAG, "Unresolvable implicit intent is set, pi=" + pendingIntent);
+        String packageName = (pendingIntent != null && pendingIntent.getIntent() != null)
+                ? pendingIntent.getIntent().getPackage() : null;
+        return new MediaButtonReceiverHolder(userId, pendingIntent,
+                packageName != null ? packageName : "");
+    }
+
+    private MediaButtonReceiverHolder(int userId, PendingIntent pendingIntent,
+            ComponentName componentName, @ComponentType int componentType) {
+        mUserId = userId;
+        mPendingIntent = pendingIntent;
+        mComponentName = componentName;
+        mPackageName = componentName.getPackageName();
+        mComponentType = componentType;
+    }
+
+    private MediaButtonReceiverHolder(int userId, PendingIntent pendingIntent, String packageName) {
+        mUserId = userId;
+        mPendingIntent = pendingIntent;
+        mComponentName = null;
+        mPackageName = packageName;
+        mComponentType = COMPONENT_TYPE_INVALID;
+    }
+
+    /**
+     * @return the user id
+     */
+    public int getUserId() {
+        return mUserId;
+    }
+
+    /**
+     * @return package name that the media button receiver would be sent to.
+     */
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Sends the media key event to the media button receiver.
+     * <p>
+     * This prioritizes using use pending intent for sending media key event.
+     *
+     * @param context context to be used to call PendingIntent#send
+     * @param keyEvent keyEvent to send
+     * @param resultCode result code to be used to call PendingIntent#send
+     *                   Ignored if there's no valid pending intent.
+     * @param onFinishedListener callback to be used to get result of PendingIntent#send.
+     *                           Ignored if there's no valid pending intent.
+     * @param handler handler to be used to call onFinishedListener
+     *                Ignored if there's no valid pending intent.
+     * @see PendingIntent#send(Context, int, Intent, PendingIntent.OnFinished, Handler)
+     */
+    public boolean send(Context context, KeyEvent keyEvent, String callingPackageName,
+            int resultCode, PendingIntent.OnFinished onFinishedListener, Handler handler) {
+        Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+        mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+        // TODO: Find a way to also send PID/UID in secure way.
+        mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callingPackageName);
+
+        if (mPendingIntent != null) {
+            if (DEBUG_KEY_EVENT) {
+                Log.d(TAG, "Sending " + keyEvent + " to the last known PendingIntent "
+                        + mPendingIntent);
+            }
+            try {
+                mPendingIntent.send(
+                        context, resultCode, mediaButtonIntent, onFinishedListener, handler);
+            } catch (PendingIntent.CanceledException e) {
+                Log.w(TAG, "Error sending key event to media button receiver " + mPendingIntent, e);
+                return false;
+            }
+        } else if (mComponentName != null) {
+            if (DEBUG_KEY_EVENT) {
+                Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
+                        + mComponentName + ", type=" + mComponentType);
+            }
+            mediaButtonIntent.setComponent(mComponentName);
+            UserHandle userHandle = UserHandle.of(mUserId);
+            try {
+                switch (mComponentType) {
+                    case COMPONENT_TYPE_ACTIVITY:
+                        context.startActivityAsUser(mediaButtonIntent, userHandle);
+                        break;
+                    case COMPONENT_TYPE_SERVICE:
+                        context.startForegroundServiceAsUser(mediaButtonIntent,
+                                userHandle);
+                        break;
+                    default:
+                        // Legacy behavior for other cases.
+                        context.sendBroadcastAsUser(mediaButtonIntent, userHandle);
+                }
+            } catch (Exception e) {
+                Log.w(TAG, "Error sending media button to the restored intent "
+                        + mComponentName + ", type=" + mComponentType, e);
+                return false;
+            }
+        } else {
+            // Leave log, just in case.
+            Log.e(TAG, "Shouldn't be happen -- pending intent or component name must be set");
+            return false;
+        }
+        return true;
+    }
+
+
+    @Override
+    public String toString() {
+        if (mPendingIntent != null) {
+            return "MBR {pi=" + mPendingIntent + ", type=" + mComponentType + "}";
+        }
+        return "Restored MBR {component=" + mComponentName + ", type=" + mComponentType + "}";
+    }
+
+    /**
+     * @return flattened string. Can be empty string if the MBR is created with implicit intent.
+     */
+    public String flattenToString() {
+        if (mComponentName == null) {
+            // We don't know which component would receive the key event.
+            return "";
+        }
+        return String.join(COMPONENT_NAME_USER_ID_DELIM,
+                mComponentName.toString(),
+                String.valueOf(mUserId),
+                String.valueOf(mComponentType));
+    }
+
+    /**
+     * Gets the type of the component
+     *
+     * @param context context
+     * @param componentName component name
+     * @return A component type
+     */
+    @ComponentType
+    private static int getComponentType(Context context, ComponentName componentName) {
+        if (componentName == null) {
+            return COMPONENT_TYPE_INVALID;
+        }
+        PackageManager pm = context.getPackageManager();
+        try {
+            ActivityInfo activityInfo = pm.getActivityInfo(componentName,
+                    PackageManager.MATCH_DIRECT_BOOT_AWARE
+                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                            | PackageManager.GET_ACTIVITIES);
+            if (activityInfo != null) {
+                return COMPONENT_TYPE_ACTIVITY;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+        }
+        try {
+            ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
+                    PackageManager.MATCH_DIRECT_BOOT_AWARE
+                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                            | PackageManager.GET_SERVICES);
+            if (serviceInfo != null) {
+                return COMPONENT_TYPE_SERVICE;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+        }
+        // Pick legacy behavior for BroadcastReceiver or unknown.
+        return COMPONENT_TYPE_BROADCAST;
+    }
+
+    private static ComponentName resolveImplicitServiceIntent(PackageManager pm, Intent intent) {
+        // Flag explanations.
+        // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
+        //     filter apps regardless of the phone's locked/unlocked state.
+        // - GET_SERVICES: Return service
+        return createComponentName(pm.resolveService(intent,
+                PackageManager.MATCH_DIRECT_BOOT_AWARE
+                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                        | PackageManager.GET_SERVICES));
+    }
+
+    private static ComponentName resolveManifestDeclaredBroadcastReceiverIntent(
+            PackageManager pm, Intent intent) {
+        // Flag explanations.
+        // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
+        //     filter apps regardless of the phone's locked/unlocked state.
+        List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(intent,
+                PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+        return (resolveInfos != null && !resolveInfos.isEmpty())
+                ? createComponentName(resolveInfos.get(0)) : null;
+    }
+
+    private static ComponentName resolveImplicitActivityIntent(PackageManager pm, Intent intent) {
+        // Flag explanations.
+        // - MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE:
+        //     Filter apps regardless of the phone's locked/unlocked state.
+        // - MATCH_DEFAULT_ONLY:
+        //     Implicit intent receiver should be set as default. Only needed for activity.
+        // - GET_ACTIVITIES: Return activity
+        return createComponentName(pm.resolveActivity(intent,
+                PackageManager.MATCH_DIRECT_BOOT_AWARE
+                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
+                        | PackageManager.MATCH_DEFAULT_ONLY
+                        | PackageManager.GET_ACTIVITIES));
+    }
+
+    private static ComponentName createComponentName(ResolveInfo resolveInfo) {
+        if (resolveInfo == null) {
+            return null;
+        }
+        ComponentInfo componentInfo;
+        // Code borrowed from ResolveInfo#getComponentInfo().
+        if (resolveInfo.activityInfo != null) {
+            componentInfo = resolveInfo.activityInfo;
+        } else if (resolveInfo.serviceInfo != null) {
+            componentInfo = resolveInfo.serviceInfo;
+        } else {
+            // We're not interested in content provider.
+            return null;
+        }
+        // Code borrowed from ComponentInfo#getComponentName().
+        try {
+            return new ComponentName(componentInfo.packageName, componentInfo.name);
+        } catch (IllegalArgumentException | NullPointerException e) {
+            // This may be happen if resolveActivity() end up with matching multiple activities.
+            // see PackageManager#resolveActivity().
+            return null;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index d08fb71..dd536ec 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -411,6 +411,12 @@
             mActiveConnection.dispose();
             mActiveConnection = null;
             setAndNotifyProviderState(null);
+            synchronized (mLock) {
+                for (RoutingSessionInfo sessionInfo : mSessionInfos) {
+                    mCallback.onSessionReleased(this, sessionInfo);
+                }
+                mSessionInfos.clear();
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 7bcbcd4..9f47b34 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -127,7 +127,7 @@
             new ArrayList<>();
 
     private long mFlags;
-    private PendingIntent mMediaButtonReceiver;
+    private MediaButtonReceiverHolder mMediaButtonReceiverHolder;
     private PendingIntent mLaunchIntent;
 
     // TransportPerformer fields
@@ -220,8 +220,8 @@
      *
      * @return The pending intent set by the app or null.
      */
-    public PendingIntent getMediaButtonReceiver() {
-        return mMediaButtonReceiver;
+    public MediaButtonReceiverHolder getMediaButtonReceiver() {
+        return mMediaButtonReceiverHolder;
     }
 
     /**
@@ -471,7 +471,7 @@
                 + ", userId=" + mUserId);
         pw.println(indent + "package=" + mPackageName);
         pw.println(indent + "launchIntent=" + mLaunchIntent);
-        pw.println(indent + "mediaButtonReceiver=" + mMediaButtonReceiver);
+        pw.println(indent + "mediaButtonReceiver=" + mMediaButtonReceiverHolder);
         pw.println(indent + "active=" + mIsActive);
         pw.println(indent + "flags=" + mFlags);
         pw.println(indent + "rating type=" + mRatingType);
@@ -833,12 +833,14 @@
 
         @Override
         public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException {
-            if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER) == 1) {
-                return;
-            }
-            mMediaButtonReceiver = pi;
             final long token = Binder.clearCallingIdentity();
             try {
+                if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
+                        != 0) {
+                    return;
+                }
+                mMediaButtonReceiverHolder =
+                        MediaButtonReceiverHolder.create(mContext, mUserId, pi);
                 mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -1529,5 +1531,4 @@
             msg.sendToTarget();
         }
     }
-
 }
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 88b884e..7ffac06 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -18,22 +18,17 @@
 
 import static android.os.UserHandle.USER_ALL;
 
-import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.INotificationManager;
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
-import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.media.AudioManager;
@@ -99,7 +94,7 @@
     private static final String TAG = "MediaSessionService";
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     // Leave log for key event always.
-    private static final boolean DEBUG_KEY_EVENT = true;
+    static final boolean DEBUG_KEY_EVENT = true;
 
     private static final int WAKELOCK_TIMEOUT = 5000;
     private static final int MEDIA_KEY_LISTENER_TIMEOUT = 1000;
@@ -760,12 +755,6 @@
      * <p>The contents of this object is guarded by {@link #mLock}.
      */
     final class FullUserRecord implements MediaSessionStack.OnMediaButtonSessionChangedListener {
-        public static final int COMPONENT_TYPE_INVALID = 0;
-        public static final int COMPONENT_TYPE_BROADCAST = 1;
-        public static final int COMPONENT_TYPE_ACTIVITY = 2;
-        public static final int COMPONENT_TYPE_SERVICE = 3;
-        private static final String COMPONENT_NAME_USER_ID_DELIM = ",";
-
         private final int mFullUserId;
         private final MediaSessionStack mPriorityStack;
         private final HashMap<IBinder, OnMediaKeyEventDispatchedListenerRecord>
@@ -774,10 +763,7 @@
                 mOnMediaKeyEventSessionChangedListeners = new HashMap<>();
         private final SparseIntArray mUidToSessionCount = new SparseIntArray();
 
-        private PendingIntent mLastMediaButtonReceiver;
-        private ComponentName mRestoredMediaButtonReceiver;
-        private int mRestoredMediaButtonReceiverComponentType;
-        private int mRestoredMediaButtonReceiverUserId;
+        private MediaButtonReceiverHolder mLastMediaButtonReceiverHolder;
 
         private IOnVolumeKeyLongPressListener mOnVolumeKeyLongPressListener;
         private int mOnVolumeKeyLongPressListenerUid;
@@ -794,21 +780,9 @@
             // Restore the remembered media button receiver before the boot.
             String mediaButtonReceiverInfo = Settings.Secure.getStringForUser(mContentResolver,
                     Settings.System.MEDIA_BUTTON_RECEIVER, mFullUserId);
-            if (mediaButtonReceiverInfo == null) {
-                return;
-            }
-            String[] tokens = mediaButtonReceiverInfo.split(COMPONENT_NAME_USER_ID_DELIM);
-            if (tokens == null || (tokens.length != 2 && tokens.length != 3)) {
-                return;
-            }
-            mRestoredMediaButtonReceiver = ComponentName.unflattenFromString(tokens[0]);
-            mRestoredMediaButtonReceiverUserId = Integer.parseInt(tokens[1]);
-            if (tokens.length == 3) {
-                mRestoredMediaButtonReceiverComponentType = Integer.parseInt(tokens[2]);
-            } else {
-                mRestoredMediaButtonReceiverComponentType =
-                        getComponentType(mRestoredMediaButtonReceiver);
-            }
+            mLastMediaButtonReceiverHolder =
+                    MediaButtonReceiverHolder.unflattenFromString(
+                            mContext, mediaButtonReceiverInfo);
         }
 
         public void destroySessionsForUserLocked(int userId) {
@@ -892,10 +866,7 @@
                     : mOnMediaKeyEventSessionChangedListeners.values()) {
                 pw.println(indent + "  from " + getCallingPackageName(cr.uid));
             }
-            pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiver);
-            pw.println(indent + "Restored MediaButtonReceiver: " + mRestoredMediaButtonReceiver);
-            pw.println(indent + "Restored MediaButtonReceiverComponentType: "
-                    + mRestoredMediaButtonReceiverComponentType);
+            pw.println(indent + "Last MediaButtonReceiver: " + mLastMediaButtonReceiverHolder);
             mPriorityStack.dump(pw, indent);
         }
 
@@ -924,25 +895,12 @@
                 return;
             }
             MediaSessionRecord sessionRecord = (MediaSessionRecord) record;
-            PendingIntent receiver = sessionRecord.getMediaButtonReceiver();
-            mLastMediaButtonReceiver = receiver;
-            mRestoredMediaButtonReceiver = null;
-            mRestoredMediaButtonReceiverComponentType = COMPONENT_TYPE_INVALID;
-
-            String mediaButtonReceiverInfo = "";
-            if (receiver != null) {
-                ComponentName component = receiver.getIntent().getComponent();
-                if (component != null
-                        && record.getPackageName().equals(component.getPackageName())) {
-                    String componentName = component.flattenToString();
-                    int componentType = getComponentType(component);
-                    mediaButtonReceiverInfo = String.join(COMPONENT_NAME_USER_ID_DELIM,
-                            componentName, String.valueOf(record.getUserId()),
-                            String.valueOf(componentType));
-                }
-            }
+            mLastMediaButtonReceiverHolder = sessionRecord.getMediaButtonReceiver();
+            String mediaButtonReceiverInfo = (mLastMediaButtonReceiverHolder == null)
+                    ? "" : mLastMediaButtonReceiverHolder.flattenToString();
             Settings.Secure.putStringForUser(mContentResolver,
-                    Settings.System.MEDIA_BUTTON_RECEIVER, mediaButtonReceiverInfo,
+                    Settings.System.MEDIA_BUTTON_RECEIVER,
+                    mediaButtonReceiverInfo,
                     mFullUserId);
         }
 
@@ -958,15 +916,9 @@
                     } else {
                         // TODO(jaewan): Implement
                     }
-                } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
-                    callback.onMediaKeyEventSessionChanged(
-                            mCurrentFullUserRecord.mLastMediaButtonReceiver
-                                    .getIntent().getComponent().getPackageName(),
-                            null);
-                } else if (mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
-                    callback.onMediaKeyEventSessionChanged(
-                            mCurrentFullUserRecord.mRestoredMediaButtonReceiver.getPackageName(),
-                            null);
+                } else if (mCurrentFullUserRecord.mLastMediaButtonReceiverHolder != null) {
+                    String packageName = mLastMediaButtonReceiverHolder.getPackageName();
+                    callback.onMediaKeyEventSessionChanged(packageName, null);
                 }
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to pushAddressedPlayerChangedLocked", e);
@@ -985,35 +937,6 @@
                     ? mGlobalPrioritySession : mPriorityStack.getMediaButtonSession();
         }
 
-        private int getComponentType(@Nullable ComponentName componentName) {
-            if (componentName == null) {
-                return COMPONENT_TYPE_INVALID;
-            }
-            PackageManager pm = mContext.getPackageManager();
-            try {
-                ActivityInfo activityInfo = pm.getActivityInfo(componentName,
-                        PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                                | PackageManager.GET_ACTIVITIES);
-                if (activityInfo != null) {
-                    return COMPONENT_TYPE_ACTIVITY;
-                }
-            } catch (NameNotFoundException e) {
-            }
-            try {
-                ServiceInfo serviceInfo = pm.getServiceInfo(componentName,
-                        PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                                | PackageManager.GET_SERVICES);
-                if (serviceInfo != null) {
-                    return COMPONENT_TYPE_SERVICE;
-                }
-            } catch (NameNotFoundException e) {
-            }
-            // Pick legacy behavior for BroadcastReceiver or unknown.
-            return COMPONENT_TYPE_BROADCAST;
-        }
-
         final class OnMediaKeyEventDispatchedListenerRecord implements IBinder.DeathRecipient {
             public final IOnMediaKeyEventDispatchedListener callback;
             public final int uid;
@@ -2166,79 +2089,31 @@
                 } catch (RemoteException e) {
                     Log.w(TAG, "Failed to send callback", e);
                 }
-            } else if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null
-                    || mCurrentFullUserRecord.mRestoredMediaButtonReceiver != null) {
+            } else if (mCurrentFullUserRecord.mLastMediaButtonReceiverHolder != null) {
                 if (needWakeLock) {
                     mKeyEventReceiver.acquireWakeLockLocked();
                 }
-                Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
-                mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-                mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
-                // TODO: Find a way to also send PID/UID in secure way.
-                String callerPackageName =
+                String callingPackageName =
                         (asSystemService) ? mContext.getPackageName() : packageName;
-                mediaButtonIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, callerPackageName);
-                try {
-                    if (mCurrentFullUserRecord.mLastMediaButtonReceiver != null) {
-                        PendingIntent receiver = mCurrentFullUserRecord.mLastMediaButtonReceiver;
-                        if (DEBUG_KEY_EVENT) {
-                            Log.d(TAG, "Sending " + keyEvent
-                                    + " to the last known PendingIntent " + receiver);
-                        }
-                        receiver.send(mContext,
-                                needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
-                                mediaButtonIntent, mKeyEventReceiver, mHandler);
-                        ComponentName componentName = mCurrentFullUserRecord
-                                .mLastMediaButtonReceiver.getIntent().getComponent();
-                        if (componentName != null) {
-                            for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
-                                    : mCurrentFullUserRecord
-                                    .mOnMediaKeyEventDispatchedListeners.values()) {
-                                cr.callback.onMediaKeyEventDispatched(keyEvent,
-                                        componentName.getPackageName(), null);
-                            }
-                        }
-                    } else {
-                        ComponentName receiver =
-                                mCurrentFullUserRecord.mRestoredMediaButtonReceiver;
-                        int componentType = mCurrentFullUserRecord
-                                .mRestoredMediaButtonReceiverComponentType;
-                        UserHandle userHandle = UserHandle.of(mCurrentFullUserRecord
-                                .mRestoredMediaButtonReceiverUserId);
-                        if (DEBUG_KEY_EVENT) {
-                            Log.d(TAG, "Sending " + keyEvent + " to the restored intent "
-                                    + receiver + ", type=" + componentType);
-                        }
-                        mediaButtonIntent.setComponent(receiver);
+
+                MediaButtonReceiverHolder mediaButtonReceiverHolder =
+                        mCurrentFullUserRecord.mLastMediaButtonReceiverHolder;
+
+                boolean sent = mediaButtonReceiverHolder.send(
+                        mContext, keyEvent, callingPackageName,
+                        needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1, mKeyEventReceiver,
+                        mHandler);
+                if (sent) {
+                    String pkgName = mediaButtonReceiverHolder.getPackageName();
+                    for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
+                            : mCurrentFullUserRecord
+                            .mOnMediaKeyEventDispatchedListeners.values()) {
                         try {
-                            switch (componentType) {
-                                case FullUserRecord.COMPONENT_TYPE_ACTIVITY:
-                                    mContext.startActivityAsUser(mediaButtonIntent, userHandle);
-                                    break;
-                                case FullUserRecord.COMPONENT_TYPE_SERVICE:
-                                    mContext.startForegroundServiceAsUser(mediaButtonIntent,
-                                            userHandle);
-                                    break;
-                                default:
-                                    // Legacy behavior for other cases.
-                                    mContext.sendBroadcastAsUser(mediaButtonIntent, userHandle);
-                            }
-                        } catch (Exception e) {
-                            Log.w(TAG, "Error sending media button to the restored intent "
-                                    + receiver + ", type=" + componentType, e);
-                        }
-                        for (FullUserRecord.OnMediaKeyEventDispatchedListenerRecord cr
-                                : mCurrentFullUserRecord
-                                .mOnMediaKeyEventDispatchedListeners.values()) {
-                            cr.callback.onMediaKeyEventDispatched(keyEvent,
-                                    receiver.getPackageName(), null);
+                            cr.callback.onMediaKeyEventDispatched(keyEvent, pkgName, null);
+                        } catch (RemoteException e) {
+                            Log.w(TAG, "Failed notify key event dispatch, uid=" + cr.uid, e);
                         }
                     }
-                } catch (CanceledException e) {
-                    Log.i(TAG, "Error sending key event to media button receiver "
-                            + mCurrentFullUserRecord.mLastMediaButtonReceiver, e);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed to send callback", e);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ea77c36..7ffd899 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7463,13 +7463,15 @@
     }
 
     @GuardedBy("mNotificationLock")
-    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
+    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete,
+            @NotificationListenerService.NotificationCancelReason int reason,
             boolean wasPosted, String listenerName) {
         cancelNotificationLocked(r, sendDelete, reason, -1, -1, wasPosted, listenerName);
     }
 
     @GuardedBy("mNotificationLock")
-    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason,
+    private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete,
+            @NotificationListenerService.NotificationCancelReason int reason,
             int rank, int count, boolean wasPosted, String listenerName) {
         final String canceledKey = r.getKey();
 
@@ -7587,6 +7589,10 @@
         EventLogTags.writeNotificationCanceled(canceledKey, reason,
                 r.getLifespanMs(now), r.getFreshnessMs(now), r.getExposureMs(now),
                 rank, count, listenerName);
+        if (wasPosted) {
+            mNotificationRecordLogger.logNotificationCancelled(r, reason,
+                    r.getStats().getDismissalSurface());
+        }
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 8d8511f..fc2d9e7 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -16,10 +16,16 @@
 
 package com.android.server.notification;
 
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_CLICK;
+import static android.service.notification.NotificationListenerService.REASON_TIMEOUT;
+
 import android.annotation.Nullable;
 import android.app.Notification;
 import android.app.Person;
 import android.os.Bundle;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationStats;
 
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
@@ -44,22 +50,144 @@
             int position, int buzzBeepBlink);
 
     /**
+     * Logs a notification cancel / dismiss event using UiEventReported (event ids from the
+     * NotificationCancelledEvents enum).
+     * @param r The NotificationRecord. If null, no action is taken.
+     * @param reason The reason the notification was canceled.
+     * @param dismissalSurface The surface the notification was dismissed from.
+     */
+    void logNotificationCancelled(@Nullable NotificationRecord r,
+            @NotificationListenerService.NotificationCancelReason int reason,
+            @NotificationStats.DismissalSurface int dismissalSurface);
+
+    /**
      * The UiEvent enums that this class can log.
      */
-    enum NotificationReportedEvents implements UiEventLogger.UiEventEnum {
-        INVALID(0),
+    enum NotificationReportedEvent implements UiEventLogger.UiEventEnum {
         @UiEvent(doc = "New notification enqueued to post")
         NOTIFICATION_POSTED(162),
-        @UiEvent(doc = "Notification substantially updated")
+        @UiEvent(doc = "Notification substantially updated, or alerted again.")
         NOTIFICATION_UPDATED(163);
 
         private final int mId;
-        NotificationReportedEvents(int id) {
+        NotificationReportedEvent(int id) {
             mId = id;
         }
         @Override public int getId() {
             return mId;
         }
+
+        public static NotificationReportedEvent fromRecordPair(NotificationRecordPair p) {
+            return (p.old != null) ? NotificationReportedEvent.NOTIFICATION_UPDATED :
+                            NotificationReportedEvent.NOTIFICATION_POSTED;
+        }
+    }
+
+    enum NotificationCancelledEvent implements UiEventLogger.UiEventEnum {
+        INVALID(0),
+        @UiEvent(doc = "Notification was canceled due to a notification click.")
+        NOTIFICATION_CANCEL_CLICK(164),
+        @UiEvent(doc = "Notification was canceled due to a user dismissal, surface not specified.")
+        NOTIFICATION_CANCEL_USER_OTHER(165),
+        @UiEvent(doc = "Notification was canceled due to a user dismiss-all (from the notification"
+                + " shade).")
+        NOTIFICATION_CANCEL_USER_CANCEL_ALL(166),
+        @UiEvent(doc = "Notification was canceled due to an inflation error.")
+        NOTIFICATION_CANCEL_ERROR(167),
+        @UiEvent(doc = "Notification was canceled by the package manager modifying the package.")
+        NOTIFICATION_CANCEL_PACKAGE_CHANGED(168),
+        @UiEvent(doc = "Notification was canceled by the owning user context being stopped.")
+        NOTIFICATION_CANCEL_USER_STOPPED(169),
+        @UiEvent(doc = "Notification was canceled by the user banning the package.")
+        NOTIFICATION_CANCEL_PACKAGE_BANNED(170),
+        @UiEvent(doc = "Notification was canceled by the app canceling this specific notification.")
+        NOTIFICATION_CANCEL_APP_CANCEL(171),
+        @UiEvent(doc = "Notification was canceled by the app cancelling all its notifications.")
+        NOTIFICATION_CANCEL_APP_CANCEL_ALL(172),
+        @UiEvent(doc = "Notification was canceled by a listener reporting a user dismissal.")
+        NOTIFICATION_CANCEL_LISTENER_CANCEL(173),
+        @UiEvent(doc = "Notification was canceled by a listener reporting a user dismiss all.")
+        NOTIFICATION_CANCEL_LISTENER_CANCEL_ALL(174),
+        @UiEvent(doc = "Notification was canceled because it was a member of a canceled group.")
+        NOTIFICATION_CANCEL_GROUP_SUMMARY_CANCELED(175),
+        @UiEvent(doc = "Notification was canceled because it was an invisible member of a group.")
+        NOTIFICATION_CANCEL_GROUP_OPTIMIZATION(176),
+        @UiEvent(doc = "Notification was canceled by the device administrator suspending the "
+                + "package.")
+        NOTIFICATION_CANCEL_PACKAGE_SUSPENDED(177),
+        @UiEvent(doc = "Notification was canceled by the owning managed profile being turned off.")
+        NOTIFICATION_CANCEL_PROFILE_TURNED_OFF(178),
+        @UiEvent(doc = "Autobundled summary notification was canceled because its group was "
+                + "unbundled")
+        NOTIFICATION_CANCEL_UNAUTOBUNDLED(179),
+        @UiEvent(doc = "Notification was canceled by the user banning the channel.")
+        NOTIFICATION_CANCEL_CHANNEL_BANNED(180),
+        @UiEvent(doc = "Notification was snoozed.")
+        NOTIFICATION_CANCEL_SNOOZED(181),
+        @UiEvent(doc = "Notification was canceled due to timeout")
+        NOTIFICATION_CANCEL_TIMEOUT(182),
+        // Values 183-189 reserved for future system dismissal reasons
+        @UiEvent(doc = "Notification was canceled due to user dismissal of a peeking notification.")
+        NOTIFICATION_CANCEL_USER_PEEK(190),
+        @UiEvent(doc = "Notification was canceled due to user dismissal from the always-on display")
+        NOTIFICATION_CANCEL_USER_AOD(191),
+        @UiEvent(doc = "Notification was canceled due to user dismissal from the notification"
+                + " shade.")
+        NOTIFICATION_CANCEL_USER_SHADE(192),
+        @UiEvent(doc = "Notification was canceled due to user dismissal from the lockscreen")
+        NOTIFICATION_CANCEL_USER_LOCKSCREEN(193);
+
+        private final int mId;
+        NotificationCancelledEvent(int id) {
+            mId = id;
+        }
+        @Override public int getId() {
+            return mId;
+        }
+        public static NotificationCancelledEvent fromCancelReason(
+                @NotificationListenerService.NotificationCancelReason int reason,
+                @NotificationStats.DismissalSurface int surface) {
+            // Shouldn't be possible to get a non-dismissed notification here.
+            if (surface == NotificationStats.DISMISSAL_NOT_DISMISSED) {
+                if (NotificationManagerService.DBG) {
+                    throw new IllegalArgumentException("Unexpected surface " + surface);
+                }
+                return INVALID;
+            }
+            // Most cancel reasons do not have a meaningful surface. Reason codes map directly
+            // to NotificationCancelledEvent codes.
+            if (surface == NotificationStats.DISMISSAL_OTHER) {
+                if ((REASON_CLICK <= reason) && (reason <= REASON_TIMEOUT)) {
+                    return NotificationCancelledEvent.values()[reason];
+                }
+                if (NotificationManagerService.DBG) {
+                    throw new IllegalArgumentException("Unexpected cancel reason " + reason);
+                }
+                return INVALID;
+            }
+            // User cancels have a meaningful surface, which we differentiate by. See b/149038335
+            // for caveats.
+            if (reason != REASON_CANCEL) {
+                if (NotificationManagerService.DBG) {
+                    throw new IllegalArgumentException("Unexpected cancel with surface " + reason);
+                }
+                return INVALID;
+            }
+            switch (surface) {
+                case NotificationStats.DISMISSAL_PEEK:
+                    return NOTIFICATION_CANCEL_USER_PEEK;
+                case NotificationStats.DISMISSAL_AOD:
+                    return NOTIFICATION_CANCEL_USER_AOD;
+                case NotificationStats.DISMISSAL_SHADE:
+                    return NOTIFICATION_CANCEL_USER_SHADE;
+                default:
+                    if (NotificationManagerService.DBG) {
+                        throw new IllegalArgumentException("Unexpected surface for user-dismiss "
+                                + reason);
+                    }
+                    return INVALID;
+            }
+        }
     }
 
     /**
@@ -88,7 +216,8 @@
                 return true;
             }
 
-            return !(Objects.equals(r.getSbn().getChannelIdLogTag(), old.getSbn().getChannelIdLogTag())
+            return !(Objects.equals(r.getSbn().getChannelIdLogTag(),
+                        old.getSbn().getChannelIdLogTag())
                     && Objects.equals(r.getSbn().getGroupLogTag(), old.getSbn().getGroupLogTag())
                     && (r.getSbn().getNotification().isGroupSummary()
                         == old.getSbn().getNotification().isGroupSummary())
@@ -97,11 +226,6 @@
                     && (r.getImportance() == old.getImportance()));
         }
 
-        NotificationReportedEvents getUiEvent() {
-            return (old != null) ? NotificationReportedEvents.NOTIFICATION_UPDATED :
-                    NotificationReportedEvents.NOTIFICATION_POSTED;
-        }
-
         /**
          * @return hash code for the notification style class, or 0 if none exists.
          */
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index 4974c30..015d280 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.server.notification;
 
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.internal.util.FrameworkStatsLog;
 
 /**
@@ -24,6 +26,8 @@
  */
 public class NotificationRecordLoggerImpl implements NotificationRecordLogger {
 
+    UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
+
     @Override
     public void logNotificationReported(NotificationRecord r, NotificationRecord old,
             int position, int buzzBeepBlink) {
@@ -32,7 +36,7 @@
             return;
         }
         FrameworkStatsLog.write(FrameworkStatsLog.NOTIFICATION_REPORTED,
-                /* int32 event_id = 1 */ p.getUiEvent().getId(),
+                /* int32 event_id = 1 */ NotificationReportedEvent.fromRecordPair(p).getId(),
                 /* int32 uid = 2 */ r.getUid(),
                 /* string package_name = 3 */ r.getSbn().getPackageName(),
                 /* int32 instance_id = 4 */ p.getInstanceId(),
@@ -61,9 +65,10 @@
         );
     }
 
-
-
-
-
-
+    @Override
+    public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) {
+        mUiEventLogger.logWithInstanceId(
+                NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface),
+                r.getUid(), r.getSbn().getPackageName(), r.getSbn().getInstanceId());
+    }
 }
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index d75eb6d..3c31f6a 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().broadcastIntentWithFeature(null, null, intent,
-                            null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE,
-                            null, false, false, userId);
+                    ActivityManager.getService().broadcastIntent(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/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 8c13b5b..d629b54 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -33,7 +33,6 @@
 import android.content.pm.parsing.ComponentParseUtils.ParsedIntentInfo;
 import android.content.pm.parsing.ComponentParseUtils.ParsedProvider;
 import android.content.pm.parsing.ComponentParseUtils.ParsedService;
-import android.net.Uri;
 import android.os.Binder;
 import android.os.Process;
 import android.os.Trace;
@@ -87,10 +86,10 @@
     private final SparseSetArray<Integer> mQueriesViaPackage = new SparseSetArray<>();
 
     /**
-     * A mapping from the set of App IDs that query others via intent to the list
-     * of packages that the intents resolve to.
+     * A mapping from the set of App IDs that query others via component match to the list
+     * of packages that the they resolve to.
      */
-    private final SparseSetArray<Integer> mQueriesViaIntent = new SparseSetArray<>();
+    private final SparseSetArray<Integer> mQueriesViaComponent = new SparseSetArray<>();
 
     /**
      * A set of App IDs that are always queryable by any package, regardless of their manifest
@@ -206,16 +205,19 @@
     }
 
     /** Returns true if the querying package may query for the potential target package */
-    private static boolean canQueryViaIntent(AndroidPackage querying,
+    private static boolean canQueryViaComponents(AndroidPackage querying,
             AndroidPackage potentialTarget) {
-        if (querying.getQueriesIntents() == null) {
-            return false;
-        }
-        for (Intent intent : querying.getQueriesIntents()) {
-            if (matches(intent, potentialTarget)) {
-                return true;
+        if (querying.getQueriesIntents() != null) {
+            for (Intent intent : querying.getQueriesIntents()) {
+                if (matchesIntentFilters(intent, potentialTarget)) {
+                    return true;
+                }
             }
         }
+        if (querying.getQueriesProviders() != null
+                && matchesProviders(querying.getQueriesProviders(), potentialTarget)) {
+            return true;
+        }
         return false;
     }
 
@@ -238,24 +240,25 @@
         return false;
     }
 
-    private static boolean matches(Intent intent, AndroidPackage potentialTarget) {
+    private static boolean matchesProviders(
+            Set<String> queriesAuthorities, AndroidPackage potentialTarget) {
         for (int p = ArrayUtils.size(potentialTarget.getProviders()) - 1; p >= 0; p--) {
             ParsedProvider provider = potentialTarget.getProviders().get(p);
             if (!provider.isExported()) {
                 continue;
             }
-            final Uri data = intent.getData();
-            if (!"content".equalsIgnoreCase(intent.getScheme()) || data == null
-                    || provider.getAuthority() == null) {
-                continue;
-            }
-            StringTokenizer authorities = new StringTokenizer(provider.getAuthority(), ";", false);
+            StringTokenizer authorities = new StringTokenizer(provider.getAuthority(), ";",
+                    false);
             while (authorities.hasMoreElements()) {
-                if (Objects.equals(authorities.nextElement(), data.getAuthority())) {
+                if (queriesAuthorities.contains(authorities.nextToken())) {
                     return true;
                 }
             }
         }
+        return false;
+    }
+
+    private static boolean matchesIntentFilters(Intent intent, AndroidPackage potentialTarget) {
         for (int s = ArrayUtils.size(potentialTarget.getServices()) - 1; s >= 0; s--) {
             ParsedService service = potentialTarget.getServices().get(s);
             if (!service.exported) {
@@ -368,8 +371,8 @@
                 final AndroidPackage existingPkg = existingSetting.pkg;
                 // let's evaluate the ability of already added packages to see this new package
                 if (!newIsForceQueryable) {
-                    if (canQueryViaIntent(existingPkg, newPkg)) {
-                        mQueriesViaIntent.add(existingSetting.appId, newPkgSetting.appId);
+                    if (canQueryViaComponents(existingPkg, newPkg)) {
+                        mQueriesViaComponent.add(existingSetting.appId, newPkgSetting.appId);
                     }
                     if (canQueryViaPackage(existingPkg, newPkg)
                             || canQueryAsInstaller(existingSetting, newPkg)) {
@@ -378,8 +381,8 @@
                 }
                 // now we'll evaluate our new package's ability to see existing packages
                 if (!mForceQueryable.contains(existingSetting.appId)) {
-                    if (canQueryViaIntent(newPkg, existingPkg)) {
-                        mQueriesViaIntent.add(newPkgSetting.appId, existingSetting.appId);
+                    if (canQueryViaComponents(newPkg, existingPkg)) {
+                        mQueriesViaComponent.add(newPkgSetting.appId, existingSetting.appId);
                     }
                     if (canQueryViaPackage(newPkg, existingPkg)
                             || canQueryAsInstaller(newPkgSetting, existingPkg)) {
@@ -427,9 +430,9 @@
             }
         }
 
-        mQueriesViaIntent.remove(setting.appId);
-        for (int i = mQueriesViaIntent.size() - 1; i >= 0; i--) {
-            mQueriesViaIntent.remove(mQueriesViaIntent.keyAt(i), setting.appId);
+        mQueriesViaComponent.remove(setting.appId);
+        for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) {
+            mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), setting.appId);
         }
         mQueriesViaPackage.remove(setting.appId);
         for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) {
@@ -594,10 +597,10 @@
                 Trace.endSection();
             }
             try {
-                Trace.beginSection("mQueriesViaIntent");
-                if (mQueriesViaIntent.contains(callingAppId, targetAppId)) {
+                Trace.beginSection("mQueriesViaComponent");
+                if (mQueriesViaComponent.contains(callingAppId, targetAppId)) {
                     if (DEBUG_LOGGING) {
-                        log(callingSetting, targetPkgSetting, "queries intent");
+                        log(callingSetting, targetPkgSetting, "queries component");
                     }
                     return false;
                 }
@@ -732,7 +735,7 @@
         pw.println("  queries via package name:");
         dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, "    ", expandPackages);
         pw.println("  queries via intent:");
-        dumpQueriesMap(pw, filteringAppId, mQueriesViaIntent, "    ", expandPackages);
+        dumpQueriesMap(pw, filteringAppId, mQueriesViaComponent, "    ", expandPackages);
         pw.println("  queryable via interaction:");
         for (int user : users) {
             pw.append("    User ").append(Integer.toString(user)).println(":");
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index cd4485e..74d2efe 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -44,7 +44,9 @@
 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;
@@ -52,8 +54,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.server.LocalServices;
+import com.android.server.appop.AppOpsService;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.util.ArrayList;
@@ -65,7 +69,7 @@
 
     private Context mContext;
     private Injector mInjector;
-    private AppOpsManager mAppOpsManager;
+    private AppOpsService mAppOpsService;
 
     public CrossProfileAppsServiceImpl(Context context) {
         this(context, new InjectorImpl(context));
@@ -96,7 +100,6 @@
     public void startActivityAsUser(
             IApplicationThread caller,
             String callingPackage,
-            String callingFeatureId,
             ComponentName component,
             @UserIdInt int userId,
             boolean launchMainActivity) throws RemoteException {
@@ -162,7 +165,7 @@
         launchIntent.setPackage(null);
         launchIntent.setComponent(component);
         mInjector.getActivityTaskManagerInternal().startActivityAsUser(
-                caller, callingPackage, callingFeatureId, launchIntent,
+                caller, callingPackage, launchIntent,
                 launchMainActivity
                         ? ActivityOptions.makeOpenCrossProfileAppsAnimation().toBundle()
                         : null,
@@ -173,7 +176,6 @@
     public void startActivityAsUserByIntent(
             IApplicationThread caller,
             String callingPackage,
-            String callingFeatureId,
             Intent intent,
             @UserIdInt int userId) throws RemoteException {
         Objects.requireNonNull(callingPackage);
@@ -210,8 +212,8 @@
 
         verifyActivityCanHandleIntent(launchIntent, callingUid, userId);
 
-        mInjector.getActivityTaskManagerInternal().startActivityAsUser(caller, callingPackage,
-                callingFeatureId, launchIntent, /* options= */ null, userId);
+        mInjector.getActivityTaskManagerInternal().startActivityAsUser(
+                caller, callingPackage, launchIntent, /* options= */ null, userId);
     }
 
     @Override
@@ -543,11 +545,12 @@
                 permission, uid, /* owningUid= */-1, /* exported= */ true);
     }
 
-    private AppOpsManager getAppOpsManager() {
-        if (mAppOpsManager == null) {
-            mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+    private AppOpsService getAppOpsService() {
+        if (mAppOpsService == null) {
+            IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+            mAppOpsService = (AppOpsService) IAppOpsService.Stub.asInterface(b);
         }
-        return mAppOpsManager;
+        return mAppOpsService;
     }
 
     private static class InjectorImpl implements Injector {
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 0b0f139..8333ae5 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -222,7 +222,6 @@
                         sanitizedIntent,
                         failureIntent,
                         requestObj.callingPackage,
-                        requestObj.callingFeatureId,
                         requestObj.verificationBundle,
                         requestObj.resolvedType,
                         requestObj.userId,
@@ -267,7 +266,6 @@
             @NonNull Intent sanitizedIntent,
             @Nullable Intent failureIntent,
             @NonNull String callingPackage,
-            @Nullable String callingFeatureId,
             @Nullable Bundle verificationBundle,
             @NonNull String resolvedType,
             int userId,
@@ -310,10 +308,9 @@
                         onFailureIntent = failureIntent;
                     }
                     final IIntentSender failureIntentTarget = ActivityManager.getService()
-                            .getIntentSenderWithFeature(
+                            .getIntentSender(
                                     ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
-                                    callingFeatureId, null /*token*/, null /*resultWho*/,
-                                    1 /*requestCode*/,
+                                    null /*token*/, null /*resultWho*/, 1 /*requestCode*/,
                                     new Intent[] { onFailureIntent },
                                     new String[] { resolvedType },
                                     PendingIntent.FLAG_CANCEL_CURRENT
@@ -331,10 +328,9 @@
             successIntent.setLaunchToken(token);
             try {
                 final IIntentSender successIntentTarget = ActivityManager.getService()
-                        .getIntentSenderWithFeature(
+                        .getIntentSender(
                                 ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
-                                callingFeatureId, null /*token*/, null /*resultWho*/,
-                                0 /*requestCode*/,
+                                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 da07365..e9f84fd 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -16,8 +16,8 @@
 
 package com.android.server.pm;
 
+import android.Manifest.permission;
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -306,6 +306,10 @@
             final int callingUserId = injectCallingUserId();
 
             if (targetUserId == callingUserId) return true;
+            if (mContext.checkCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL)
+                    == PackageManager.PERMISSION_GRANTED) {
+                return true;
+            }
 
             long ident = injectClearCallingIdentity();
             try {
@@ -783,9 +787,8 @@
         }
 
         @Override
-        public boolean startShortcut(String callingPackage, String packageName, String featureId,
-                String shortcutId, Rect sourceBounds, Bundle startActivityOptions,
-                int targetUserId) {
+        public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
+                Rect sourceBounds, Bundle startActivityOptions, int targetUserId) {
             verifyCallingPackage(callingPackage);
             if (!canAccessProfile(targetUserId, "Cannot start activity")) {
                 return false;
@@ -809,16 +812,15 @@
             intents[0].setSourceBounds(sourceBounds);
 
             return startShortcutIntentsAsPublisher(
-                    intents, packageName, featureId, startActivityOptions, targetUserId);
+                    intents, packageName, startActivityOptions, targetUserId);
         }
 
         private boolean startShortcutIntentsAsPublisher(@NonNull Intent[] intents,
-                @NonNull String publisherPackage, @Nullable String publishedFeatureId,
-                Bundle startActivityOptions, int userId) {
+                @NonNull String publisherPackage, Bundle startActivityOptions, int userId) {
             final int code;
             try {
                 code = mActivityTaskManagerInternal.startActivitiesAsPackage(publisherPackage,
-                        publishedFeatureId, userId, intents, startActivityOptions);
+                        userId, intents, startActivityOptions);
                 if (ActivityManager.isStartResultSuccessful(code)) {
                     return true; // Success
                 } else {
@@ -873,8 +875,8 @@
 
         @Override
         public void startSessionDetailsActivityAsUser(IApplicationThread caller,
-                String callingPackage, String callingFeatureId, SessionInfo sessionInfo,
-                Rect sourceBounds, Bundle opts, UserHandle userHandle) throws RemoteException {
+                String callingPackage, SessionInfo sessionInfo, Rect sourceBounds,
+                Bundle opts, UserHandle userHandle) throws RemoteException {
             int userId = userHandle.getIdentifier();
             if (!canAccessProfile(userId, "Cannot start details activity")) {
                 return;
@@ -890,13 +892,13 @@
                             .authority(callingPackage).build());
             i.setSourceBounds(sourceBounds);
 
-            mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
-                    callingFeatureId, i, opts, userId);
+            mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage, i, opts,
+                    userId);
         }
 
         @Override
         public void startActivityAsUser(IApplicationThread caller, String callingPackage,
-                String callingFeatureId, ComponentName component, Rect sourceBounds,
+                ComponentName component, Rect sourceBounds,
                 Bundle opts, UserHandle user) throws RemoteException {
             if (!canAccessProfile(user.getIdentifier(), "Cannot start activity")) {
                 return;
@@ -950,12 +952,12 @@
                 Binder.restoreCallingIdentity(ident);
             }
             mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
-                    callingFeatureId, launchIntent, opts, user.getIdentifier());
+                    launchIntent, opts, user.getIdentifier());
         }
 
         @Override
         public void showAppDetailsAsUser(IApplicationThread caller,
-                String callingPackage, String callingFeatureId, ComponentName component,
+                String callingPackage, ComponentName component,
                 Rect sourceBounds, Bundle opts, UserHandle user) throws RemoteException {
             if (!canAccessProfile(user.getIdentifier(), "Cannot show app details")) {
                 return;
@@ -973,7 +975,7 @@
                 Binder.restoreCallingIdentity(ident);
             }
             mActivityTaskManagerInternal.startActivityAsUser(caller, callingPackage,
-                    callingFeatureId, intent, opts, user.getIdentifier());
+                    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/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 944280d..97defcd 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -214,7 +214,7 @@
 
     private static final String PROPERTY_NAME_INHERIT_NATIVE = "pi.inherit_native_on_dont_kill";
     private static final int[] EMPTY_CHILD_SESSION_ARRAY = EmptyArray.INT;
-    private static final FileInfo[] EMPTY_FILE_INFO_ARRAY = {};
+    private static final InstallationFile[] EMPTY_INSTALLATION_FILE_ARRAY = {};
 
     private static final String SYSTEM_DATA_LOADER_PACKAGE = "android";
 
@@ -309,33 +309,8 @@
     @GuardedBy("mLock")
     private int mParentSessionId;
 
-    static class FileInfo {
-        public final int location;
-        public final String name;
-        public final Long lengthBytes;
-        public final byte[] metadata;
-        public final byte[] signature;
-
-        public static FileInfo added(int location, String name, Long lengthBytes, byte[] metadata,
-                byte[] signature) {
-            return new FileInfo(location, name, lengthBytes, metadata, signature);
-        }
-
-        public static FileInfo removed(int location, String name) {
-            return new FileInfo(location, name, -1L, null, null);
-        }
-
-        FileInfo(int location, String name, Long lengthBytes, byte[] metadata, byte[] signature) {
-            this.location = location;
-            this.name = name;
-            this.lengthBytes = lengthBytes;
-            this.metadata = metadata;
-            this.signature = signature;
-        }
-    }
-
     @GuardedBy("mLock")
-    private ArrayList<FileInfo> mFiles = new ArrayList<>();
+    private ArrayList<InstallationFile> mFiles = new ArrayList<>();
 
     @GuardedBy("mLock")
     private boolean mStagedSessionApplied;
@@ -508,7 +483,7 @@
             PackageSessionProvider sessionProvider, Looper looper, StagingManager stagingManager,
             int sessionId, int userId, int installerUid, @NonNull InstallSource installSource,
             SessionParams params, long createdMillis,
-            File stageDir, String stageCid, FileInfo[] files, boolean prepared,
+            File stageDir, String stageCid, InstallationFile[] files, boolean prepared,
             boolean committed, boolean sealed,
             @Nullable int[] childSessionIds, int parentSessionId, boolean isReady,
             boolean isFailed, boolean isApplied, int stagedSessionErrorCode,
@@ -539,7 +514,7 @@
         this.mParentSessionId = parentSessionId;
 
         if (files != null) {
-            for (FileInfo file : files) {
+            for (InstallationFile file : files) {
                 mFiles.add(file);
             }
         }
@@ -738,7 +713,7 @@
 
         String[] result = new String[mFiles.size()];
         for (int i = 0, size = mFiles.size(); i < size; ++i) {
-            result[i] = mFiles.get(i).name;
+            result[i] = mFiles.get(i).getName();
         }
         return result;
     }
@@ -2425,7 +2400,7 @@
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotSealedLocked("addFile");
 
-            mFiles.add(FileInfo.added(location, name, lengthBytes, metadata, signature));
+            mFiles.add(new InstallationFile(location, name, lengthBytes, metadata, signature));
         }
     }
 
@@ -2443,7 +2418,7 @@
             assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotSealedLocked("removeFile");
 
-            mFiles.add(FileInfo.removed(location, getRemoveMarkerName(name)));
+            mFiles.add(new InstallationFile(location, getRemoveMarkerName(name), -1, null, null));
         }
     }
 
@@ -2461,17 +2436,16 @@
         }
 
         final List<InstallationFile> addedFiles = new ArrayList<>(mFiles.size());
-        for (FileInfo file : mFiles) {
-            if (sAddedFilter.accept(new File(this.stageDir, file.name))) {
-                addedFiles.add(new InstallationFile(
-                        file.name, file.lengthBytes, file.metadata));
+        for (InstallationFile file : mFiles) {
+            if (sAddedFilter.accept(new File(this.stageDir, file.getName()))) {
+                addedFiles.add(file);
             }
         }
         final List<String> removedFiles = new ArrayList<>(mFiles.size());
-        for (FileInfo file : mFiles) {
-            if (sRemovedFilter.accept(new File(this.stageDir, file.name))) {
-                String name = file.name.substring(
-                        0, file.name.length() - REMOVE_MARKER_EXTENSION.length());
+        for (InstallationFile file : mFiles) {
+            if (sRemovedFilter.accept(new File(this.stageDir, file.getName()))) {
+                String name = file.getName().substring(
+                        0, file.getName().length() - REMOVE_MARKER_EXTENSION.length());
                 removedFiles.add(name);
             }
         }
@@ -2970,13 +2944,13 @@
                 writeIntAttribute(out, ATTR_SESSION_ID, childSessionId);
                 out.endTag(null, TAG_CHILD_SESSION);
             }
-            for (FileInfo fileInfo : mFiles) {
+            for (InstallationFile file : mFiles) {
                 out.startTag(null, TAG_SESSION_FILE);
-                writeIntAttribute(out, ATTR_LOCATION, fileInfo.location);
-                writeStringAttribute(out, ATTR_NAME, fileInfo.name);
-                writeLongAttribute(out, ATTR_LENGTH_BYTES, fileInfo.lengthBytes);
-                writeByteArrayAttribute(out, ATTR_METADATA, fileInfo.metadata);
-                writeByteArrayAttribute(out, ATTR_SIGNATURE, fileInfo.signature);
+                writeIntAttribute(out, ATTR_LOCATION, file.getLocation());
+                writeStringAttribute(out, ATTR_NAME, file.getName());
+                writeLongAttribute(out, ATTR_LENGTH_BYTES, file.getLengthBytes());
+                writeByteArrayAttribute(out, ATTR_METADATA, file.getMetadata());
+                writeByteArrayAttribute(out, ATTR_SIGNATURE, file.getSignature());
                 out.endTag(null, TAG_SESSION_FILE);
             }
         }
@@ -3088,7 +3062,7 @@
         List<String> grantedRuntimePermissions = new ArrayList<>();
         List<String> whitelistedRestrictedPermissions = new ArrayList<>();
         List<Integer> childSessionIds = new ArrayList<>();
-        List<FileInfo> files = new ArrayList<>();
+        List<InstallationFile> files = new ArrayList<>();
         int outerDepth = in.getDepth();
         int type;
         while ((type = in.next()) != XmlPullParser.END_DOCUMENT
@@ -3107,7 +3081,7 @@
                 childSessionIds.add(readIntAttribute(in, ATTR_SESSION_ID, SessionInfo.INVALID_ID));
             }
             if (TAG_SESSION_FILE.equals(in.getName())) {
-                files.add(new FileInfo(
+                files.add(new InstallationFile(
                         readIntAttribute(in, ATTR_LOCATION, 0),
                         readStringAttribute(in, ATTR_NAME),
                         readLongAttribute(in, ATTR_LENGTH_BYTES, -1),
@@ -3135,16 +3109,16 @@
             childSessionIdsArray = EMPTY_CHILD_SESSION_ARRAY;
         }
 
-        FileInfo[] fileInfosArray = null;
+        InstallationFile[] fileArray = null;
         if (!files.isEmpty()) {
-            fileInfosArray = files.toArray(EMPTY_FILE_INFO_ARRAY);
+            fileArray = files.toArray(EMPTY_INSTALLATION_FILE_ARRAY);
         }
 
         InstallSource installSource = InstallSource.create(installInitiatingPackageName,
                 installOriginatingPackageName, installerPackageName);
         return new PackageInstallerSession(callback, context, pm, sessionProvider,
                 installerThread, stagingManager, sessionId, userId, installerUid,
-                installSource, params, createdMillis, stageDir, stageCid, fileInfosArray,
+                installSource, params, createdMillis, stageDir, stageCid, fileArray,
                 prepared, committed, sealed, childSessionIdsArray, parentSessionId,
                 isReady, isFailed, isApplied, stagedSessionErrorCode, stagedSessionErrorMessage);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2c85d06..88c048c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6298,11 +6298,10 @@
 
     private void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
             Intent origIntent, String resolvedType, String callingPackage,
-            @Nullable String callingFeatureId, boolean isRequesterInstantApp,
-            Bundle verificationBundle, int userId) {
+            boolean isRequesterInstantApp, Bundle verificationBundle, int userId) {
         final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO,
                 new InstantAppRequest(responseObj, origIntent, resolvedType,
-                        callingPackage, callingFeatureId, isRequesterInstantApp, userId, verificationBundle,
+                        callingPackage, isRequesterInstantApp, userId, verificationBundle,
                         false /*resolveForStart*/, responseObj.hostDigestPrefixSecure,
                         responseObj.token));
         mHandler.sendMessage(msg);
@@ -6988,10 +6987,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*/,
-                        null /*callingFeatureId*/, isRequesterInstantApp, userId,
-                        null /*verificationBundle*/, resolveForStart,
+                final InstantAppRequest requestObject = new InstantAppRequest(
+                        null /*responseObj*/, intent /*origIntent*/, resolvedType,
+                        null /*callingPackage*/, isRequesterInstantApp,
+                        userId, null /*verificationBundle*/, resolveForStart,
                         digest.getDigestPrefixSecure(), token);
                 auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(
                         mInstantAppResolverConnection, requestObject);
@@ -12207,7 +12206,7 @@
                         + intent.toShortString(false, true, false, false)
                         + " " + intent.getExtras(), here);
             }
-            am.broadcastIntentWithFeature(null, null, intent, null, finishedReceiver,
+            am.broadcastIntent(null, intent, null, finishedReceiver,
                     0, null, null, requiredPermissions, android.app.AppOpsManager.OP_NONE,
                     null, finishedReceiver != null, false, id);
         }
@@ -12369,9 +12368,8 @@
                 lockedBcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
             }
             final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED};
-            am.broadcastIntentWithFeature(null, null, lockedBcIntent, null, null, 0, null, null,
-                    requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
-                    userId);
+            am.broadcastIntent(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)) {
@@ -12379,9 +12377,8 @@
                 if (includeStopped) {
                     bcIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
                 }
-                am.broadcastIntentWithFeature(null, null, bcIntent, null, null, 0, null, null,
-                        requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
-                        userId);
+                am.broadcastIntent(null, bcIntent, null, null, 0, null, null, requiredPermissions,
+                        android.app.AppOpsManager.OP_NONE, null, false, false, userId);
             }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -18784,7 +18781,7 @@
             intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
             try {
-                am.broadcastIntentWithFeature(null, null, intent, null, null,
+                am.broadcastIntent(null, intent, null, null,
                         0, null, null, null, android.app.AppOpsManager.OP_NONE,
                         null, false, false, userId);
             } catch (RemoteException e) {
@@ -23428,10 +23425,9 @@
         @Override
         public void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
                 Intent origIntent, String resolvedType, String callingPackage,
-                @Nullable String callingFeatureId, boolean isRequesterInstantApp,
-                Bundle verificationBundle, int userId) {
-            PackageManagerService.this.requestInstantAppResolutionPhaseTwo(responseObj, origIntent,
-                    resolvedType, callingPackage, callingFeatureId, isRequesterInstantApp,
+                boolean isRequesterInstantApp, Bundle verificationBundle, int userId) {
+            PackageManagerService.this.requestInstantAppResolutionPhaseTwo(
+                    responseObj, origIntent, resolvedType, callingPackage, isRequesterInstantApp,
                     verificationBundle, userId);
         }
 
@@ -24341,9 +24337,8 @@
                 Manifest.permission.RECEIVE_DEVICE_CUSTOMIZATION_READY,
             };
             try {
-                am.broadcastIntentWithFeature(null, null, intent, null, null, 0, null, null,
-                        requiredPermissions, android.app.AppOpsManager.OP_NONE, null, false, false,
-                        UserHandle.USER_ALL);
+                am.broadcastIntent(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/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index cb94043..c69a62d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1163,7 +1163,7 @@
         final InstallParams params = makeInstallParams();
         if (params.sessionParams.dataLoaderParams == null) {
             params.sessionParams.setDataLoaderParams(
-                    PackageManagerShellCommandDataLoader.getDataLoaderParams(this));
+                    PackageManagerShellCommandDataLoader.getStreamingDataLoaderParams(this));
         }
         return doRunInstall(params);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
index 5dca9e1..4170be4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommandDataLoader.java
@@ -54,7 +54,7 @@
 
     private static final String STDIN_PATH = "-";
 
-    static DataLoaderParams getDataLoaderParams(ShellCommand shellCommand) {
+    private static String getDataLoaderParamsArgs(ShellCommand shellCommand) {
         int commandId;
         synchronized (sShellCommands) {
             // Clean up old references.
@@ -78,8 +78,12 @@
             sShellCommands.put(commandId, new WeakReference<>(shellCommand));
         }
 
-        final String args = SHELL_COMMAND_ID_PREFIX + commandId;
-        return DataLoaderParams.forStreaming(new ComponentName(PACKAGE, CLASS), args);
+        return SHELL_COMMAND_ID_PREFIX + commandId;
+    }
+
+    static DataLoaderParams getStreamingDataLoaderParams(ShellCommand shellCommand) {
+        return DataLoaderParams.forStreaming(new ComponentName(PACKAGE, CLASS),
+                getDataLoaderParamsArgs(shellCommand));
     }
 
     private static int extractShellCommandId(String args) {
@@ -133,17 +137,17 @@
                 return false;
             }
             try {
-                for (InstallationFile fileInfo : addedFiles) {
-                    String filePath = new String(fileInfo.getMetadata(), StandardCharsets.UTF_8);
+                for (InstallationFile file : addedFiles) {
+                    String filePath = new String(file.getMetadata(), StandardCharsets.UTF_8);
                     if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) {
                         final ParcelFileDescriptor inFd = ParcelFileDescriptor.dup(
                                 shellCommand.getInFileDescriptor());
-                        mConnector.writeData(fileInfo.getName(), 0, fileInfo.getSize(), inFd);
+                        mConnector.writeData(file.getName(), 0, file.getLengthBytes(), inFd);
                     } else {
                         ParcelFileDescriptor incomingFd = null;
                         try {
                             incomingFd = shellCommand.openFileForSystem(filePath, "r");
-                            mConnector.writeData(fileInfo.getName(), 0, incomingFd.getStatSize(),
+                            mConnector.writeData(file.getName(), 0, incomingFd.getStatSize(),
                                     incomingFd);
                         } finally {
                             IoUtils.closeQuietly(incomingFd);
@@ -159,7 +163,8 @@
     }
 
     @Override
-    public DataLoaderService.DataLoader onCreateDataLoader() {
+    public DataLoaderService.DataLoader onCreateDataLoader(
+            @NonNull DataLoaderParams dataLoaderParams) {
         return new DataLoader();
     }
 }
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 77bb48e..4c40448 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -89,7 +89,7 @@
      * <ul>
      * <li> 0  - disable whitelist (install all system packages; no logging)</li>
      * <li> 1  - enforce (only install system packages if they are whitelisted)</li>
-     * <li> 2  - log (log when a non-whitelisted package is run)</li>
+     * <li> 2  - log (log non-whitelisted packages)</li>
      * <li> 4  - for all users: implicitly whitelist any package not mentioned in the whitelist</li>
      * <li> 8  - for SYSTEM: implicitly whitelist any package not mentioned in the whitelist</li>
      * <li> 16 - ignore OTAs (don't install system packages during OTAs)</li>
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4b2dc85..ede04f3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5195,8 +5195,7 @@
                     final Intent dock = createHomeDockIntent();
                     if (dock != null) {
                         int result = ActivityTaskManager.getService()
-                                .startActivityAsUser(null, mContext.getBasePackageName(),
-                                        mContext.getFeatureId(), dock,
+                                .startActivityAsUser(null, mContext.getBasePackageName(), dock,
                                         dock.resolveTypeIfNeeded(mContext.getContentResolver()),
                                         null, null, 0,
                                         ActivityManager.START_FLAG_ONLY_IF_NEEDED,
@@ -5207,8 +5206,7 @@
                     }
                 }
                 int result = ActivityTaskManager.getService()
-                        .startActivityAsUser(null, mContext.getBasePackageName(),
-                                mContext.getFeatureId(), mHomeIntent,
+                        .startActivityAsUser(null, mContext.getBasePackageName(), mHomeIntent,
                                 mHomeIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
                                 null, null, 0,
                                 ActivityManager.START_FLAG_ONLY_IF_NEEDED,
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index 81ec466..a83c58d 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -27,16 +27,22 @@
 import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
-import static java.lang.Integer.min;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.os.Build;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.internal.compat.IPlatformCompat;
 
 /**
  * The behavior of soft restricted permissions is different for each permission. This class collects
@@ -46,6 +52,27 @@
  * {@link com.android.packageinstaller.permission.utils.SoftRestrictedPermissionPolicy}
  */
 public abstract class SoftRestrictedPermissionPolicy {
+    /**
+     * Enables scoped storage, with exceptions for apps that explicitly request legacy access, or
+     * apps that hold the {@code android.Manifest.permission#WRITE_MEDIA_STORAGE} permission.
+     * See https://developer.android.com/training/data-storage#scoped-storage for more information.
+     */
+    @ChangeId
+    // This change is enabled for apps with targetSDK > {@link android.os.Build.VERSION_CODES.P}
+    @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.P)
+    static final long ENABLE_SCOPED_STORAGE = 144914977L;
+
+    /**
+     * Enforces scoped storage for all apps, preventing individual apps from opting out. This change
+     * has precedence over {@code ENABLE_SCOPED_STORAGE}.
+     */
+    @ChangeId
+    // This change is enabled for apps with targetSDK > {@link android.os.Build.VERSION_CODES.Q}.
+    @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.Q)
+    static final long REQUIRE_SCOPED_STORAGE = 131432978L;
+
+    private static final String LOG_TAG = SoftRestrictedPermissionPolicy.class.getSimpleName();
+
     private static final int FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT =
             FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
                     | FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
@@ -60,41 +87,6 @@
             };
 
     /**
-     * TargetSDK is per package. To make sure two apps int the same shared UID do not fight over
-     * what to set, always compute the combined targetSDK.
-     *
-     * @param context A context
-     * @param appInfo The app that is changed
-     * @param user The user the app belongs to
-     *
-     * @return The minimum targetSDK of all apps sharing the uid of the app
-     */
-    private static int getMinimumTargetSDK(@NonNull Context context,
-            @NonNull ApplicationInfo appInfo, @NonNull UserHandle user) {
-        PackageManager pm = context.getPackageManager();
-
-        int minimumTargetSDK = appInfo.targetSdkVersion;
-
-        String[] uidPkgs = pm.getPackagesForUid(appInfo.uid);
-        if (uidPkgs != null) {
-            for (String uidPkg : uidPkgs) {
-                if (!uidPkg.equals(appInfo.packageName)) {
-                    ApplicationInfo uidPkgInfo;
-                    try {
-                        uidPkgInfo = pm.getApplicationInfoAsUser(uidPkg, 0, user);
-                    } catch (PackageManager.NameNotFoundException e) {
-                        continue;
-                    }
-
-                    minimumTargetSDK = min(minimumTargetSDK, uidPkgInfo.targetSdkVersion);
-                }
-            }
-        }
-
-        return minimumTargetSDK;
-    }
-
-    /**
      * Get the policy for a soft restricted permission.
      *
      * @param context A context to use
@@ -114,26 +106,31 @@
             case READ_EXTERNAL_STORAGE: {
                 final boolean isWhiteListed;
                 boolean shouldApplyRestriction;
-                final int targetSDK;
                 final boolean hasRequestedLegacyExternalStorage;
                 final boolean hasWriteMediaStorageGrantedForUid;
+                final boolean isScopedStorageEnabled;
 
                 if (appInfo != null) {
                     PackageManager pm = context.getPackageManager();
                     int flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
                     isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
-                    shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
-                    targetSDK = getMinimumTargetSDK(context, appInfo, user);
                     hasRequestedLegacyExternalStorage = hasUidRequestedLegacyExternalStorage(
                             appInfo.uid, context);
                     hasWriteMediaStorageGrantedForUid = hasWriteMediaStorageGrantedForUid(
                             appInfo.uid, context);
+                    final boolean isScopedStorageRequired =
+                            isChangeEnabledForUid(context, appInfo, user, REQUIRE_SCOPED_STORAGE);
+                    isScopedStorageEnabled =
+                            isChangeEnabledForUid(context, appInfo, user, ENABLE_SCOPED_STORAGE)
+                            || isScopedStorageRequired;
+                    shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0
+                            || isScopedStorageRequired;
                 } else {
                     isWhiteListed = false;
                     shouldApplyRestriction = false;
-                    targetSDK = 0;
                     hasRequestedLegacyExternalStorage = false;
                     hasWriteMediaStorageGrantedForUid = false;
+                    isScopedStorageEnabled = false;
                 }
 
                 // We have a check in PermissionPolicyService.PermissionToOpSynchroniser.setUidMode
@@ -143,7 +140,7 @@
                 return new SoftRestrictedPermissionPolicy() {
                     @Override
                     public boolean mayGrantPermission() {
-                        return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
+                        return isWhiteListed || isScopedStorageEnabled;
                     }
                     @Override
                     public int getExtraAppOpCode() {
@@ -151,7 +148,7 @@
                     }
                     @Override
                     public boolean mayAllowExtraAppOp() {
-                        return !shouldApplyRestriction && targetSDK <= Build.VERSION_CODES.Q
+                        return !shouldApplyRestriction
                                 && (hasRequestedLegacyExternalStorage
                                         || hasWriteMediaStorageGrantedForUid);
                     }
@@ -163,22 +160,26 @@
             }
             case WRITE_EXTERNAL_STORAGE: {
                 final boolean isWhiteListed;
-                final int targetSDK;
+                final boolean isScopedStorageEnabled;
 
                 if (appInfo != null) {
                     final int flags = context.getPackageManager().getPermissionFlags(permission,
                             appInfo.packageName, user);
                     isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
-                    targetSDK = getMinimumTargetSDK(context, appInfo, user);
+                    final boolean isScopedStorageRequired =
+                            isChangeEnabledForUid(context, appInfo, user, REQUIRE_SCOPED_STORAGE);
+                    isScopedStorageEnabled =
+                            isChangeEnabledForUid(context, appInfo, user, ENABLE_SCOPED_STORAGE)
+                            || isScopedStorageRequired;
                 } else {
                     isWhiteListed = false;
-                    targetSDK = 0;
+                    isScopedStorageEnabled = false;
                 }
 
                 return new SoftRestrictedPermissionPolicy() {
                     @Override
                     public boolean mayGrantPermission() {
-                        return isWhiteListed || targetSDK >= Build.VERSION_CODES.Q;
+                        return isWhiteListed || isScopedStorageEnabled;
                     }
                 };
             }
@@ -187,6 +188,62 @@
         }
     }
 
+    /**
+     * Checks whether an AppCompat change is enabled for all packages sharing a UID with the
+     * provided application.
+     *
+     * @param context A context to use.
+     * @param appInfo The application for which to check whether the compat change is enabled.
+     * @param user The user the app belongs to.
+     * @param changeId A {@link android.compat.annotation.ChangeId} corresponding to the change.
+     *
+     * @return true if this change is enabled for all apps sharing the UID of the provided app,
+     *         false otherwise.
+     */
+    private static boolean isChangeEnabledForUid(@NonNull Context context,
+            @NonNull ApplicationInfo appInfo, @NonNull UserHandle user, long changeId) {
+        PackageManager pm = context.getPackageManager();
+
+        String[] uidPackages = pm.getPackagesForUid(appInfo.uid);
+        if (uidPackages != null) {
+            for (String uidPackage : uidPackages) {
+                ApplicationInfo uidPackageInfo;
+                try {
+                    uidPackageInfo = pm.getApplicationInfoAsUser(uidPackage, 0, user);
+                } catch (PackageManager.NameNotFoundException e) {
+                    continue;
+                }
+                if (!isChangeEnabled(uidPackageInfo, changeId)) {
+                    // At least one package sharing this UID does not have this change enabled.
+                    return false;
+                }
+            }
+            // All packages sharing this UID returned true for {@link #isChangeEnabled()}.
+            return true;
+        } else {
+            Log.w(LOG_TAG, "Check for change " + changeId + " for uid " + appInfo.uid
+                    + " produced no packages. Defaulting to using the information for "
+                    + appInfo.packageName + " only.");
+            return isChangeEnabled(appInfo, changeId);
+        }
+    }
+
+    private static boolean isChangeEnabled(@NonNull ApplicationInfo appInfo, long changeId) {
+        IBinder binder = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
+        IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(binder);
+
+        final long callingId = Binder.clearCallingIdentity();
+
+        try {
+            return platformCompat.isChangeEnabled(changeId, appInfo);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Check for change " + changeId + " failed. Defaulting to enabled.", e);
+            return true;
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
     private static boolean hasUidRequestedLegacyExternalStorage(int uid, @NonNull Context context) {
         PackageManager packageManager = context.getPackageManager();
         String[] packageNames = packageManager.getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index e77839c..524ae54 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -224,6 +224,13 @@
     }
 
     /**
+     * Deletes all device-encrypted apex data snapshots for the given rollback id.
+     */
+    public void destroyApexDeSnapshots(int rollbackId) {
+        mApexManager.destroyDeSnapshots(rollbackId);
+    }
+
+    /**
      * Commits the pending backups and restores for a given {@code userId} and {@code rollback}. If
      * the rollback has a pending backup, it is updated with a mapping from {@code userId} to inode
      * of the CE user data snapshot.
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index b5da1c2..5abd9f0 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -44,7 +44,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.pm.ApexManager;
 
 import java.io.File;
 import java.io.IOException;
@@ -671,7 +670,7 @@
                 }
             }
             if (containsApex) {
-                ApexManager.getInstance().destroyDeSnapshots(info.getRollbackId());
+                dataHelper.destroyApexDeSnapshots(info.getRollbackId());
             }
 
             RollbackStore.deleteRollback(this);
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index 34d2c16..58455ca 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -36,7 +36,7 @@
 import android.service.textclassifier.TextClassifierService;
 import android.service.textclassifier.TextClassifierService.ConnectionState;
 import android.text.TextUtils;
-import android.util.LruCache;
+import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.textclassifier.ConversationActions;
@@ -65,6 +65,7 @@
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Queue;
 
@@ -146,11 +147,7 @@
     private final Object mLock;
     @GuardedBy("mLock")
     final SparseArray<UserState> mUserStates = new SparseArray<>();
-    // SystemTextClassifier.onDestroy() is not guaranteed to be called, use LruCache here
-    // to avoid leak.
-    @GuardedBy("mLock")
-    private final LruCache<TextClassificationSessionId, TextClassificationContext>
-            mSessionContextCache = new LruCache<>(40);
+    private final SessionCache mSessionCache;
     private final TextClassificationConstants mSettings;
     @Nullable
     private final String mDefaultTextClassifierPackage;
@@ -165,6 +162,7 @@
         PackageManager packageManager = mContext.getPackageManager();
         mDefaultTextClassifierPackage = packageManager.getDefaultTextClassifierPackageName();
         mSystemTextClassifierPackage = packageManager.getSystemTextClassifierPackageName();
+        mSessionCache = new SessionCache(mLock);
     }
 
     private void startListenSettings() {
@@ -314,7 +312,7 @@
                 classificationContext.getUseDefaultTextClassifier(),
                 service -> {
                     service.onCreateTextClassificationSession(classificationContext, sessionId);
-                    mSessionContextCache.put(sessionId, classificationContext);
+                    mSessionCache.put(sessionId, classificationContext);
                 },
                 "onCreateTextClassificationSession",
                 NO_OP_CALLBACK);
@@ -326,14 +324,14 @@
         Objects.requireNonNull(sessionId);
 
         synchronized (mLock) {
-            TextClassificationContext textClassificationContext =
-                    mSessionContextCache.get(sessionId);
+            final StrippedTextClassificationContext textClassificationContext =
+                    mSessionCache.get(sessionId);
             final int userId = textClassificationContext != null
-                    ? textClassificationContext.getUserId()
+                    ? textClassificationContext.userId
                     : UserHandle.getCallingUserId();
             final boolean useDefaultTextClassifier =
                     textClassificationContext != null
-                            ? textClassificationContext.getUseDefaultTextClassifier()
+                            ? textClassificationContext.useDefaultTextClassifier
                             : true;
             handleRequest(
                     userId,
@@ -342,7 +340,7 @@
                     useDefaultTextClassifier,
                     service -> {
                         service.onDestroyTextClassificationSession(sessionId);
-                        mSessionContextCache.remove(sessionId);
+                        mSessionCache.remove(sessionId);
                     },
                     "onDestroyTextClassificationSession",
                     NO_OP_CALLBACK);
@@ -409,7 +407,7 @@
                     pw.decreaseIndent();
                 }
             }
-            pw.println("Number of active sessions: " + mSessionContextCache.size());
+            pw.println("Number of active sessions: " + mSessionCache.size());
         }
     }
 
@@ -568,6 +566,81 @@
         }
     }
 
+    /**
+     * Stores the stripped down version of {@link TextClassificationContext}s, i.e. {@link
+     * StrippedTextClassificationContext},  keyed by {@link TextClassificationSessionId}. Sessions
+     * are cleaned up automatically when the client process is dead.
+     */
+    static final class SessionCache {
+        @NonNull
+        private final Object mLock;
+        @NonNull
+        @GuardedBy("mLock")
+        private final Map<TextClassificationSessionId, StrippedTextClassificationContext> mCache =
+                new ArrayMap<>();
+        @NonNull
+        @GuardedBy("mLock")
+        private final Map<TextClassificationSessionId, DeathRecipient> mDeathRecipients =
+                new ArrayMap<>();
+
+        SessionCache(@NonNull Object lock) {
+            mLock = Objects.requireNonNull(lock);
+        }
+
+        void put(@NonNull TextClassificationSessionId sessionId,
+                @NonNull TextClassificationContext textClassificationContext) {
+            synchronized (mLock) {
+                mCache.put(sessionId,
+                        new StrippedTextClassificationContext(textClassificationContext));
+                try {
+                    DeathRecipient deathRecipient = () -> remove(sessionId);
+                    sessionId.getToken().linkToDeath(deathRecipient, /* flags= */ 0);
+                    mDeathRecipients.put(sessionId, deathRecipient);
+                } catch (RemoteException e) {
+                    Slog.w(LOG_TAG, "SessionCache: Failed to link to death", e);
+                }
+            }
+        }
+
+        @Nullable
+        StrippedTextClassificationContext get(@NonNull TextClassificationSessionId sessionId) {
+            Objects.requireNonNull(sessionId);
+            synchronized (mLock) {
+                return mCache.get(sessionId);
+            }
+        }
+
+        void remove(@NonNull TextClassificationSessionId sessionId) {
+            Objects.requireNonNull(sessionId);
+            synchronized (mLock) {
+                DeathRecipient deathRecipient = mDeathRecipients.get(sessionId);
+                if (deathRecipient != null) {
+                    sessionId.getToken().unlinkToDeath(deathRecipient, /* flags= */ 0);
+                }
+                mDeathRecipients.remove(sessionId);
+                mCache.remove(sessionId);
+            }
+        }
+
+        int size() {
+            synchronized (mLock) {
+                return mCache.size();
+            }
+        }
+    }
+
+    /** A stripped down version of {@link TextClassificationContext}. */
+    static class StrippedTextClassificationContext {
+        @UserIdInt
+        public final int userId;
+        public final boolean useDefaultTextClassifier;
+
+        StrippedTextClassificationContext(TextClassificationContext textClassificationContext) {
+            userId = textClassificationContext.getUserId();
+            useDefaultTextClassifier = textClassificationContext.getUseDefaultTextClassifier();
+        }
+    }
+
     private final class UserState {
         @UserIdInt
         final int mUserId;
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 8c54fa9..88de340 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -87,11 +87,15 @@
     private static final long SYSTEM_CLOCK_PARANOIA_THRESHOLD_MILLIS = 2 * 1000;
 
     /**
-     * The number of previous telephony suggestions to keep for each ID (for use during debugging).
+     * The number of suggestions to keep. These are logged in bug reports to assist when debugging
+     * issues with detection.
      */
-    private static final int KEEP_SUGGESTION_HISTORY_SIZE = 30;
+    private static final int KEEP_SUGGESTION_HISTORY_SIZE = 10;
 
-    // A log for changes made to the system clock and why.
+    /**
+     * A log that records the decisions / decision metadata that affected the device's system clock
+     * time. This is logged in bug reports to assist with debugging issues with detection.
+     */
     @NonNull
     private final LocalLog mTimeChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
 
@@ -140,7 +144,18 @@
         if (!validateSuggestionTime(timeSuggestion.getUtcTime(), timeSuggestion)) {
             return;
         }
-        mLastNetworkSuggestion.set(timeSuggestion);
+
+        // The caller submits suggestions with the best available information when there are network
+        // changes. The best available information may have been cached and if they were all stored
+        // this would lead to duplicates showing up in the suggestion history. The suggestions may
+        // be made for different reasons but there is not a significant benefit to storing the same
+        // suggestion information again. doAutoTimeDetection() should still be called: this ensures
+        // the suggestion and device state are always re-evaluated, which might produce a different
+        // detected time if, for example, the age of all suggestions are considered.
+        NetworkTimeSuggestion lastNetworkSuggestion = mLastNetworkSuggestion.get();
+        if (lastNetworkSuggestion == null || !lastNetworkSuggestion.equals(timeSuggestion)) {
+            mLastNetworkSuggestion.set(timeSuggestion);
+        }
 
         // Now perform auto time detection. The new suggestion may be used to modify the system
         // clock.
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index 652dbe15..cc33fb0 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -161,16 +161,17 @@
     public static final int TELEPHONY_SCORE_USAGE_THRESHOLD = TELEPHONY_SCORE_MEDIUM;
 
     /**
-     * The number of previous telephony suggestions to keep for each ID (for use during debugging).
+     * The number of suggestions to keep. These are logged in bug reports to assist when debugging
+     * issues with detection.
      */
-    private static final int KEEP_TELEPHONY_SUGGESTION_HISTORY_SIZE = 30;
+    private static final int KEEP_SUGGESTION_HISTORY_SIZE = 10;
 
     @NonNull
     private final Callback mCallback;
 
     /**
-     * A log that records the decisions / decision metadata that affected the device's time zone
-     * (for use during debugging).
+     * A log that records the decisions / decision metadata that affected the device's time zone.
+     * This is logged in bug reports to assist with debugging issues with detection.
      */
     @NonNull
     private final LocalLog mTimeZoneChangesLog = new LocalLog(30, false /* useLocalTimestamps */);
@@ -182,8 +183,7 @@
      */
     @GuardedBy("this")
     private ArrayMapWithHistory<Integer, QualifiedTelephonyTimeZoneSuggestion>
-            mSuggestionBySlotIndex =
-            new ArrayMapWithHistory<>(KEEP_TELEPHONY_SUGGESTION_HISTORY_SIZE);
+            mSuggestionBySlotIndex = new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
 
     /**
      * Creates a new instance of {@link TimeZoneDetectorStrategyImpl}.
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ad57e1f..aa6d854 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -362,7 +362,6 @@
     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_";
@@ -423,7 +422,6 @@
     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;
@@ -775,7 +773,6 @@
                 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());
@@ -1486,10 +1483,9 @@
     }
 
     ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,
-            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,
+            int _launchedFromPid, int _launchedFromUid, String _launchedFromPackage, 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,
@@ -1568,7 +1564,6 @@
         launchedFromPid = _launchedFromPid;
         launchedFromUid = _launchedFromUid;
         launchedFromPackage = _launchedFromPackage;
-        launchedFromFeatureId = _launchedFromFeature;
         shortComponentName = _intent.getComponent().flattenToShortString();
         resolvedType = _resolvedType;
         componentSpecified = _componentSpecified;
@@ -2289,7 +2284,7 @@
      * @return Whether AppOps allows this package to enter picture-in-picture.
      */
     private boolean checkEnterPictureInPictureAppOpsState() {
-        return mAtmService.getAppOpsManager().checkOpNoThrow(
+        return mAtmService.getAppOpsService().checkOperation(
                 OP_PICTURE_IN_PICTURE, info.applicationInfo.uid, packageName) == MODE_ALLOWED;
     }
 
@@ -7287,9 +7282,6 @@
         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);
         }
@@ -7317,7 +7309,6 @@
         PersistableBundle persistentState = null;
         int launchedFromUid = 0;
         String launchedFromPackage = null;
-        String launchedFromFeature = null;
         String resolvedType = null;
         boolean componentSpecified = false;
         int userId = 0;
@@ -7336,8 +7327,6 @@
                 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)) {
@@ -7385,11 +7374,10 @@
                     " resolvedType=" + resolvedType);
         }
         final ActivityRecord r = new ActivityRecord(service, null /* caller */,
-                0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, launchedFromFeature,
-                intent, resolvedType, aInfo, service.getConfiguration(), null /* resultTo */,
-                null /* resultWho */, 0 /* reqCode */, componentSpecified,
-                false /* rootVoiceInteraction */, stackSupervisor, null /* options */,
-                null /* sourceRecord */);
+                0 /* launchedFromPid */, launchedFromUid, launchedFromPackage, 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;
@@ -7686,7 +7674,7 @@
         mainWindow.getContentInsets(insets);
         InsetUtils.addInsets(insets, getLetterboxInsets());
         return new RemoteAnimationTarget(task.mTaskId, record.getMode(),
-                record.mAdapter.mCapturedLeash, !task.fillsParent(),
+                record.mAdapter.mCapturedLeash, !fillsParent(),
                 mainWindow.mWinAnimator.mLastClipRect, insets,
                 getPrefixOrderIndex(), record.mAdapter.mPosition,
                 record.mAdapter.mStackBounds, task.getWindowConfiguration(),
@@ -7704,4 +7692,9 @@
         }
         win.getAnimationFrames(outFrame, outInsets, outStableInsets, outSurfaceInsets);
     }
+
+    void setPictureInPictureParams(PictureInPictureParams p) {
+        pictureInPictureArgs.copyOnlySet(p);
+        getTask().getRootTask().setPictureInPictureParams(p);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 9030fce..d380f8c 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -135,7 +135,6 @@
 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;
@@ -623,7 +622,7 @@
                 true /*neverRelinquishIdentity*/,
                 _taskDescription != null ? _taskDescription : new ActivityManager.TaskDescription(),
                 id, INVALID_TASK_ID, INVALID_TASK_ID, 0 /*taskAffiliationColor*/,
-                info.applicationInfo.uid, info.packageName, null, info.resizeMode,
+                info.applicationInfo.uid, info.packageName, info.resizeMode,
                 info.supportsPictureInPicture(), false /*_realActivitySuspended*/,
                 false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE, info,
                 _voiceSession, _voiceInteractor, stack);
@@ -636,16 +635,15 @@
             String _lastDescription, long lastTimeMoved, boolean neverRelinquishIdentity,
             ActivityManager.TaskDescription _lastTaskDescription, int taskAffiliation,
             int prevTaskId, int nextTaskId, int taskAffiliationColor, int callingUid,
-            String callingPackage, @Nullable String callingFeatureId, int resizeMode,
-            boolean supportsPictureInPicture, boolean _realActivitySuspended,
-            boolean userSetupComplete, int minWidth, int minHeight,
+            String callingPackage, 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, callingFeatureId, resizeMode, supportsPictureInPicture,
+                callingUid, callingPackage, resizeMode, supportsPictureInPicture,
                 _realActivitySuspended, userSetupComplete, minWidth, minHeight, info, _voiceSession,
                 _voiceInteractor, stack);
 
@@ -2926,7 +2924,6 @@
                             .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 a582f21..a513ef8 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -88,7 +88,6 @@
 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;
@@ -261,9 +260,7 @@
     /** Short cut */
     private WindowManagerService mWindowManager;
 
-    private AppOpsManager mAppOpsManager;
-
-    /** Common synchronization logic used to save things to disks. */
+     /** Common synchronization logic used to save things to disks. */
     PersisterQueue mPersisterQueue;
     LaunchParamsPersister mLaunchParamsPersister;
     private LaunchParamsController mLaunchParamsController;
@@ -1050,8 +1047,8 @@
 
     boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo, String resultWho,
             int requestCode, int callingPid, int callingUid, String callingPackage,
-            @Nullable String callingFeatureId, boolean ignoreTargetSecurity,
-            boolean launchingInTask, WindowProcessController callerApp, ActivityRecord resultRecord,
+            boolean ignoreTargetSecurity, boolean launchingInTask,
+            WindowProcessController callerApp, ActivityRecord resultRecord,
             ActivityStack resultStack) {
         final boolean isCallerRecents = mService.getRecentTasks() != null
                 && mService.getRecentTasks().isCallerRecents(callingUid);
@@ -1063,10 +1060,10 @@
             // existing task, then also allow the activity to be fully relaunched.
             return true;
         }
-        final int componentRestriction = getComponentRestrictionForCallingPackage(aInfo,
-                callingPackage, callingFeatureId, callingPid, callingUid, ignoreTargetSecurity);
+        final int componentRestriction = getComponentRestrictionForCallingPackage(
+                aInfo, callingPackage, callingPid, callingUid, ignoreTargetSecurity);
         final int actionRestriction = getActionRestrictionForCallingPackage(
-                intent.getAction(), callingPackage, callingFeatureId, callingPid, callingUid);
+                intent.getAction(), callingPackage, callingPid, callingUid);
         if (componentRestriction == ACTIVITY_RESTRICTION_PERMISSION
                 || actionRestriction == ACTIVITY_RESTRICTION_PERMISSION) {
             if (resultRecord != null) {
@@ -1197,16 +1194,8 @@
         }
     }
 
-    private AppOpsManager getAppOpsManager() {
-        if (mAppOpsManager == null) {
-            mAppOpsManager = mService.mContext.getSystemService(AppOpsManager.class);
-        }
-        return mAppOpsManager;
-    }
-
     private int getComponentRestrictionForCallingPackage(ActivityInfo activityInfo,
-            String callingPackage, @Nullable String callingFeatureId, int callingPid,
-            int callingUid, boolean ignoreTargetSecurity) {
+            String callingPackage, int callingPid, int callingUid, boolean ignoreTargetSecurity) {
         if (!ignoreTargetSecurity && mService.checkComponentPermission(activityInfo.permission,
                 callingPid, callingUid, activityInfo.applicationInfo.uid, activityInfo.exported)
                 == PERMISSION_DENIED) {
@@ -1222,8 +1211,9 @@
             return ACTIVITY_RESTRICTION_NONE;
         }
 
-        if (getAppOpsManager().noteOpNoThrow(opCode, callingUid,
-                callingPackage, callingFeatureId, "") != AppOpsManager.MODE_ALLOWED) {
+        // TODO moltmann b/136595429: Set featureId from caller
+        if (mService.getAppOpsService().noteOperation(opCode, callingUid,
+                callingPackage, /* featureId */ null, false, "") != AppOpsManager.MODE_ALLOWED) {
             if (!ignoreTargetSecurity) {
                 return ACTIVITY_RESTRICTION_APPOP;
             }
@@ -1232,8 +1222,8 @@
         return ACTIVITY_RESTRICTION_NONE;
     }
 
-    private int getActionRestrictionForCallingPackage(String action, String callingPackage,
-            @Nullable String callingFeatureId, int callingPid, int callingUid) {
+    private int getActionRestrictionForCallingPackage(String action,
+            String callingPackage, int callingPid, int callingUid) {
         if (action == null) {
             return ACTIVITY_RESTRICTION_NONE;
         }
@@ -1266,8 +1256,9 @@
             return ACTIVITY_RESTRICTION_NONE;
         }
 
-        if (getAppOpsManager().noteOpNoThrow(opCode, callingUid,
-                callingPackage, callingFeatureId, "") != AppOpsManager.MODE_ALLOWED) {
+        // TODO moltmann b/136595429: Set featureId from caller
+        if (mService.getAppOpsService().noteOperation(opCode, callingUid,
+                callingPackage, /* featureId */ null, false, "") != AppOpsManager.MODE_ALLOWED) {
             return ACTIVITY_RESTRICTION_APPOP;
         }
 
@@ -2710,7 +2701,6 @@
             SafeActivityOptions options) {
         Task task = null;
         final String callingPackage;
-        final String callingFeatureId;
         final Intent intent;
         final int userId;
         int activityType = ACTIVITY_TYPE_UNDEFINED;
@@ -2790,12 +2780,11 @@
                 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, callingFeatureId, intent, null, null,
+            return mService.getActivityStartController().startActivityInPackage(
+                    task.mCallingUid, callingPid, callingUid, callingPackage, 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 881dc81..f35ba9e 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -26,7 +26,6 @@
 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;
@@ -279,11 +278,10 @@
     }
 
     final int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
-            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) {
+            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) {
 
         userId = checkTargetUser(userId, validateIncomingUser, realCallingPid, realCallingUid,
                 reason);
@@ -294,7 +292,6 @@
                 .setRealCallingPid(realCallingPid)
                 .setRealCallingUid(realCallingUid)
                 .setCallingPackage(callingPackage)
-                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setResultTo(resultTo)
                 .setResultWho(resultWho)
@@ -313,20 +310,19 @@
      *
      * @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,
-            @Nullable String callingFeatureId, Intent[] intents, String[] resolvedTypes,
-            IBinder resultTo, SafeActivityOptions options, int userId, boolean validateIncomingUser,
-            PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
+    final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
+            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
+            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
+            boolean allowBackgroundActivityStart) {
         return startActivitiesInPackage(uid, 0 /* realCallingPid */, -1 /* realCallingUid */,
-                callingPackage, callingFeatureId, intents, resolvedTypes, resultTo, options, userId,
-                validateIncomingUser, originatingPendingIntent, allowBackgroundActivityStart);
+            callingPackage, intents, resolvedTypes, resultTo, options, userId, validateIncomingUser,
+            originatingPendingIntent, allowBackgroundActivityStart);
     }
 
     /**
@@ -343,9 +339,9 @@
      *        null if not originated by PendingIntent
      */
     final int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
-            String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
-            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
-            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
+            String callingPackage, Intent[] intents, String[] resolvedTypes, IBinder resultTo,
+            SafeActivityOptions options, int userId, boolean validateIncomingUser,
+            PendingIntentRecord originatingPendingIntent,
             boolean allowBackgroundActivityStart) {
 
         final String reason = "startActivityInPackage";
@@ -354,14 +350,14 @@
                 Binder.getCallingUid(), reason);
 
         // TODO: Switch to user app stacks here.
-        return startActivities(null, uid, realCallingPid, realCallingUid, callingPackage,
-                callingFeatureId, intents, resolvedTypes, resultTo, options, userId, reason,
-                originatingPendingIntent, allowBackgroundActivityStart);
+        return startActivities(null, uid, realCallingPid, realCallingUid, callingPackage, intents,
+                resolvedTypes, resultTo, options, userId, reason, originatingPendingIntent,
+                allowBackgroundActivityStart);
     }
 
     int startActivities(IApplicationThread caller, int callingUid, int incomingRealCallingPid,
-            int incomingRealCallingUid, String callingPackage, @Nullable String callingFeatureId,
-            Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
+            int incomingRealCallingUid, String callingPackage, Intent[] intents,
+            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
             int userId, String reason, PendingIntentRecord originatingPendingIntent,
             boolean allowBackgroundActivityStart) {
         if (intents == null) {
@@ -439,7 +435,6 @@
                         .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 1009771..76aa1d1 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -34,7 +34,6 @@
 
 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;
@@ -86,7 +85,6 @@
     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
@@ -122,20 +120,19 @@
      * method should not be changed during intercept.
      */
     void setStates(int userId, int realCallingPid, int realCallingUid, int startFlags,
-            String callingPackage, @Nullable String callingFeatureId) {
+            String callingPackage) {
         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, mCallingFeatureId, callingUid, mUserId,
-                null /*token*/, null /*resultCode*/, 0 /*requestCode*/,
+                INTENT_SENDER_ACTIVITY, mCallingPackage, 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 8ed798c..c7270f2 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -318,7 +318,6 @@
         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;
@@ -368,7 +367,6 @@
             callingPid = DEFAULT_CALLING_PID;
             callingUid = DEFAULT_CALLING_UID;
             callingPackage = null;
-            callingFeatureId = null;
             realCallingPid = DEFAULT_REAL_CALLING_PID;
             realCallingUid = DEFAULT_REAL_CALLING_UID;
             startFlags = 0;
@@ -407,7 +405,6 @@
             callingPid = request.callingPid;
             callingUid = request.callingUid;
             callingPackage = request.callingPackage;
-            callingFeatureId = request.callingFeatureId;
             realCallingPid = request.realCallingPid;
             realCallingUid = request.realCallingUid;
             startFlags = request.startFlags;
@@ -696,10 +693,9 @@
         }
 
         final IIntentSender target = mService.getIntentSenderLocked(
-                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},
+                ActivityManager.INTENT_SENDER_ACTIVITY, "android" /* packageName */, 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 */);
 
@@ -811,7 +807,6 @@
         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;
@@ -887,7 +882,6 @@
                 // we want the final activity to consider it to have been launched by the
                 // previous app activity.
                 callingPackage = sourceRecord.launchedFromPackage;
-                callingFeatureId = sourceRecord.launchedFromFeatureId;
             }
         }
 
@@ -955,8 +949,8 @@
         }
 
         boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
-                requestCode, callingPid, callingUid, callingPackage, callingFeatureId,
-                request.ignoreTargetSecurity, inTask != null, callerApp, resultRecord, resultStack);
+                requestCode, callingPid, callingUid, callingPackage, request.ignoreTargetSecurity,
+                inTask != null, callerApp, resultRecord, resultStack);
         abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                 callingPid, resolvedType, aInfo.applicationInfo);
         abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,
@@ -996,8 +990,7 @@
             }
         }
 
-        mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage,
-                callingFeatureId);
+        mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage);
         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
@@ -1030,7 +1023,7 @@
             if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                     aInfo.packageName, userId)) {
                 final IIntentSender target = mService.getIntentSenderLocked(
-                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, callingFeatureId,
+                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
                         callingUid, userId, null, null, 0, new Intent[]{intent},
                         new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
                                 | PendingIntent.FLAG_ONE_SHOT, null);
@@ -1088,7 +1081,7 @@
         // app [on install success].
         if (rInfo != null && rInfo.auxiliaryInfo != null) {
             intent = createLaunchIntent(rInfo.auxiliaryInfo, request.ephemeralIntent,
-                    callingPackage, callingFeatureId, verificationBundle, resolvedType, userId);
+                    callingPackage, verificationBundle, resolvedType, userId);
             resolvedType = null;
             callingUid = realCallingUid;
             callingPid = realCallingPid;
@@ -1097,10 +1090,9 @@
         }
 
         final ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
-                callingPackage, callingFeatureId, intent, resolvedType, aInfo,
-                mService.getGlobalConfiguration(), resultRecord, resultWho, requestCode,
-                request.componentSpecified, voiceSession != null, mSupervisor, checkedOptions,
-                sourceRecord);
+                callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
+                resultRecord, resultWho, requestCode, request.componentSpecified,
+                voiceSession != null, mSupervisor, checkedOptions, sourceRecord);
         mLastStartActivityRecord = r;
 
         if (r.appTimeTracker == null && sourceRecord != null) {
@@ -1310,22 +1302,21 @@
      * Creates a launch intent for the given auxiliary resolution data.
      */
     private @NonNull Intent createLaunchIntent(@Nullable AuxiliaryResolveInfo auxiliaryResponse,
-            Intent originalIntent, String callingPackage, @Nullable String callingFeatureId,
-            Bundle verificationBundle, String resolvedType, int userId) {
+            Intent originalIntent, String callingPackage, 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,
-                    callingFeatureId, isRequesterInstantApp, verificationBundle, userId);
+                    isRequesterInstantApp, verificationBundle, userId);
         }
         return InstantAppResolver.buildEphemeralInstallerIntent(
                 originalIntent,
                 InstantAppResolver.sanitizeIntent(originalIntent),
                 auxiliaryResponse == null ? null : auxiliaryResponse.failureIntent,
                 callingPackage,
-                callingFeatureId,
                 verificationBundle,
                 resolvedType,
                 userId,
@@ -2584,11 +2575,6 @@
         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 7302e52..25f6d6f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -182,15 +182,14 @@
     public abstract void notifySingleTaskDisplayDrawn(int displayId);
 
     /**
-     * Start activity {@code intents} as if {@code packageName/featureId} on user {@code userId} did
-     * it.
+     * Start activity {@code intents} as if {@code packageName} 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, String featureId,
+    public abstract int startActivitiesAsPackage(String packageName,
             int userId, Intent[] intents, Bundle bOptions);
 
     /**
@@ -200,7 +199,6 @@
      * @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.
@@ -210,17 +208,16 @@
      *        from originatingPendingIntent
      */
     public abstract int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
-            String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
-            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
-            boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
+            String callingPackage, 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, @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);
+            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);
 
     /**
      * Start activity {@code intent} without calling user-id check.
@@ -231,7 +228,7 @@
      * @return error codes used by {@link IActivityManager#startActivity} and its siblings.
      */
     public abstract int startActivityAsUser(IApplicationThread caller, String callingPackage,
-            @Nullable String callingFeatureId, Intent intent, @Nullable Bundle options, int userId);
+            Intent intent, @Nullable Bundle options, int userId);
 
     /**
      * Called when Keyguard flags might have changed.
@@ -391,7 +388,7 @@
     public abstract ActivityTokens getTopActivityForTask(int taskId);
 
     public abstract IIntentSender getIntentSender(int type, String packageName,
-            @Nullable String featureId, int callingUid, int userId, IBinder token, String resultWho,
+            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 ea5a71a..4b96ea0 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -234,6 +234,7 @@
 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;
@@ -263,6 +264,7 @@
 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;
@@ -376,7 +378,7 @@
     RootWindowContainer mRootWindowContainer;
     WindowManagerService mWindowManager;
     private UserManagerService mUserManager;
-    private AppOpsManager mAppOpsManager;
+    private AppOpsService mAppOpsService;
     /** All active uids in the system. */
     private final MirrorActiveUids mActiveUids = new MirrorActiveUids();
     private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>();
@@ -889,11 +891,12 @@
         return mUserManager;
     }
 
-    AppOpsManager getAppOpsManager() {
-        if (mAppOpsManager == null) {
-            mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+    AppOpsService getAppOpsService() {
+        if (mAppOpsService == null) {
+            IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+            mAppOpsService = (AppOpsService) IAppOpsService.Stub.asInterface(b);
         }
-        return mAppOpsManager;
+        return mAppOpsService;
     }
 
     boolean hasUserRestriction(String restriction, int userId) {
@@ -901,8 +904,8 @@
     }
 
     boolean hasSystemAlertWindowPermission(int callingUid, int callingPid, String callingPackage) {
-        final int mode = getAppOpsManager().noteOpNoThrow(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
-                callingUid, callingPackage, /* featureId */ null, "");
+        final int mode = getAppOpsService().noteOperation(AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
+                callingUid, callingPackage, /* featureId */ null, false, "");
         if (mode == AppOpsManager.MODE_DEFAULT) {
             return checkPermission(Manifest.permission.SYSTEM_ALERT_WINDOW, callingPid, callingUid)
                     == PERMISSION_GRANTED;
@@ -1022,43 +1025,41 @@
 
     @Override
     public final int startActivity(IApplicationThread caller, String callingPackage,
-            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,
+            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,
                 UserHandle.getCallingUserId());
     }
 
     @Override
     public final int startActivities(IApplicationThread caller, String callingPackage,
-            String callingFeatureId, Intent[] intents, String[] resolvedTypes, IBinder resultTo,
-            Bundle bOptions, int userId) {
+            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,
-                callingFeatureId, intents, resolvedTypes, resultTo,
-                SafeActivityOptions.fromBundle(bOptions), userId, reason,
-                null /* originatingPendingIntent */, false /* allowBackgroundActivityStart */);
+                intents, resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId,
+                reason, null /* originatingPendingIntent */,
+                false /* allowBackgroundActivityStart */);
     }
 
     @Override
     public int startActivityAsUser(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 startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType,
-                resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, userId,
+            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,
                 true /*validateIncomingUser*/);
     }
 
     private int startActivityAsUser(IApplicationThread caller, String callingPackage,
-            @Nullable String callingFeatureId, Intent intent, String resolvedType,
-            IBinder resultTo, String resultWho, int requestCode, int startFlags,
-            ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
+            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
+            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
+            boolean validateIncomingUser) {
         assertPackageMatchesCallingUid(callingPackage);
         enforceNotIsolatedCaller("startActivityAsUser");
 
@@ -1069,7 +1070,6 @@
         return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
                 .setCaller(caller)
                 .setCallingPackage(callingPackage)
-                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setResultTo(resultTo)
                 .setResultWho(resultWho)
@@ -1215,7 +1215,6 @@
                     .setCallingPid(-1)
                     .setCallingUid(r.launchedFromUid)
                     .setCallingPackage(r.launchedFromPackage)
-                    .setCallingFeatureId(r.launchedFromFeatureId)
                     .setRealCallingPid(-1)
                     .setRealCallingUid(r.launchedFromUid)
                     .setActivityOptions(options)
@@ -1232,9 +1231,8 @@
 
     @Override
     public final WaitResult startActivityAndWait(IApplicationThread caller, String callingPackage,
-            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
-            String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo,
-            Bundle bOptions, int userId) {
+            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");
@@ -1244,7 +1242,6 @@
         getActivityStartController().obtainStarter(intent, "startActivityAndWait")
                 .setCaller(caller)
                 .setCallingPackage(callingPackage)
-                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setResultTo(resultTo)
                 .setResultWho(resultWho)
@@ -1260,9 +1257,8 @@
 
     @Override
     public final int startActivityWithConfig(IApplicationThread caller, String callingPackage,
-            String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo,
-            String resultWho, int requestCode, int startFlags, Configuration config,
-            Bundle bOptions, int userId) {
+            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,
@@ -1271,7 +1267,6 @@
         return getActivityStartController().obtainStarter(intent, "startActivityWithConfig")
                 .setCaller(caller)
                 .setCallingPackage(callingPackage)
-                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setResultTo(resultTo)
                 .setResultWho(resultWho)
@@ -1322,7 +1317,6 @@
         final ActivityRecord sourceRecord;
         final int targetUid;
         final String targetPackage;
-        final String targetFeatureId;
         final boolean isResolver;
         synchronized (mGlobalLock) {
             if (resultTo == null) {
@@ -1390,7 +1384,6 @@
             }
             targetUid = sourceRecord.launchedFromUid;
             targetPackage = sourceRecord.launchedFromPackage;
-            targetFeatureId = sourceRecord.launchedFromFeatureId;
             isResolver = sourceRecord.isResolverOrChildActivity();
         }
 
@@ -1403,7 +1396,6 @@
             return getActivityStartController().obtainStarter(intent, "startActivityAsCaller")
                     .setCallingUid(targetUid)
                     .setCallingPackage(targetPackage)
-                    .setCallingFeatureId(targetFeatureId)
                     .setResolvedType(resolvedType)
                     .setResultTo(resultTo)
                     .setResultWho(resultWho)
@@ -1439,8 +1431,8 @@
     }
 
     @Override
-    public int startVoiceActivity(String callingPackage, String callingFeatureId, int callingPid,
-            int callingUid, Intent intent, String resolvedType, IVoiceInteractionSession session,
+    public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
+            Intent intent, String resolvedType, IVoiceInteractionSession session,
             IVoiceInteractor interactor, int startFlags, ProfilerInfo profilerInfo,
             Bundle bOptions, int userId) {
         assertPackageMatchesCallingUid(callingPackage);
@@ -1453,7 +1445,6 @@
         return getActivityStartController().obtainStarter(intent, "startVoiceActivity")
                 .setCallingUid(callingUid)
                 .setCallingPackage(callingPackage)
-                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setVoiceSession(session)
                 .setVoiceInteractor(interactor)
@@ -1466,9 +1457,8 @@
     }
 
     @Override
-    public int startAssistantActivity(String callingPackage, @NonNull String callingFeatureId,
-            int callingPid, int callingUid, Intent intent, String resolvedType, Bundle bOptions,
-            int userId) {
+    public int startAssistantActivity(String callingPackage, 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");
@@ -1476,7 +1466,6 @@
         return getActivityStartController().obtainStarter(intent, "startAssistantActivity")
                 .setCallingUid(callingUid)
                 .setCallingPackage(callingPackage)
-                .setCallingFeatureId(callingFeatureId)
                 .setResolvedType(resolvedType)
                 .setActivityOptions(bOptions)
                 .setUserId(userId)
@@ -1500,14 +1489,13 @@
         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,
-                        recentsFeatureId, recentsUid, caller);
+                        recentsUid, caller);
                 if (recentsAnimationRunner == null) {
                     anim.preloadRecentsActivity();
                 } else {
@@ -2807,6 +2795,7 @@
         }
     }
 
+    // TODO(148895075): deprecate and replace with task equivalents
     @Override
     public List<ActivityManager.StackInfo> getAllStackInfos() {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
@@ -2833,6 +2822,7 @@
         }
     }
 
+    // TODO(148895075): deprecate and replace with task equivalents
     @Override
     public List<ActivityManager.StackInfo> getAllStackInfosOnDisplay(int displayId) {
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "getAllStackInfos()");
@@ -4165,7 +4155,7 @@
                             return;
                         }
                         // Only update the saved args from the args that are set
-                        r.pictureInPictureArgs.copyOnlySet(params);
+                        r.setPictureInPictureParams(params);
                         final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
                         final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
                         // Adjust the source bounds by the insets for the transition down
@@ -4213,7 +4203,7 @@
                         "setPictureInPictureParams", token, params);
 
                 // Only update the saved args from the args that are set
-                r.pictureInPictureArgs.copyOnlySet(params);
+                r.setPictureInPictureParams(params);
                 if (r.inPinnedWindowingMode()) {
                     // If the activity is already in picture-in-picture, update the pinned stack now
                     // if it is not already expanding to fullscreen. Otherwise, the arguments will
@@ -5785,9 +5775,9 @@
 
     }
 
-    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) {
+    IIntentSender getIntentSenderLocked(int type, String packageName, 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) {
@@ -5803,8 +5793,8 @@
         }
 
         final PendingIntentRecord rec = mPendingIntentController.getIntentSender(type, packageName,
-                featureId, callingUid, userId, token, resultWho, requestCode, intents,
-                resolvedTypes, flags, bOptions);
+                callingUid, userId, token, resultWho, requestCode, intents, resolvedTypes, flags,
+                bOptions);
         final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
         if (noCreate) {
             return rec;
@@ -6192,8 +6182,8 @@
         }
 
         @Override
-        public int startActivitiesAsPackage(String packageName, @Nullable String featureId,
-                int userId, Intent[] intents, Bundle bOptions) {
+        public int startActivitiesAsPackage(String packageName, int userId, Intent[] intents,
+                Bundle bOptions) {
             Objects.requireNonNull(intents, "intents");
             final String[] resolvedTypes = new String[intents.length];
 
@@ -6218,7 +6208,7 @@
             }
 
             return getActivityStartController().startActivitiesInPackage(
-                    packageUid, packageName, featureId,
+                    packageUid, packageName,
                     intents, resolvedTypes, null /* resultTo */,
                     SafeActivityOptions.fromBundle(bOptions), userId,
                     false /* validateIncomingUser */, null /* originatingPendingIntent */,
@@ -6227,41 +6217,41 @@
 
         @Override
         public int startActivitiesInPackage(int uid, int realCallingPid, int realCallingUid,
-                String callingPackage, @Nullable String callingFeatureId, Intent[] intents,
-                String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId,
-                boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
+                String callingPackage, 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, callingFeatureId, intents, resolvedTypes,
-                        resultTo, options, userId, validateIncomingUser, originatingPendingIntent,
+                        realCallingUid, callingPackage, intents, resolvedTypes, resultTo, options,
+                        userId, validateIncomingUser, originatingPendingIntent,
                         allowBackgroundActivityStart);
             }
         }
 
         @Override
         public int startActivityInPackage(int uid, int realCallingPid, int realCallingUid,
-                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,
+                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) {
             assertPackageMatchesCallingUid(callingPackage);
             synchronized (mGlobalLock) {
                 return getActivityStartController().startActivityInPackage(uid, realCallingPid,
-                        realCallingUid, callingPackage, callingFeatureId, intent, resolvedType,
-                        resultTo, resultWho, requestCode, startFlags, options, userId, inTask,
-                        reason, validateIncomingUser, originatingPendingIntent,
+                        realCallingUid, callingPackage, intent, resolvedType, resultTo, resultWho,
+                        requestCode, startFlags, options, userId, inTask, reason,
+                        validateIncomingUser, originatingPendingIntent,
                         allowBackgroundActivityStart);
             }
         }
 
         @Override
-        public int startActivityAsUser(IApplicationThread caller, String callerPackage,
-                @Nullable String callerFeatureId, Intent intent, Bundle options, int userId) {
+        public int startActivityAsUser(IApplicationThread caller, String callerPacakge,
+                Intent intent, Bundle options, int userId) {
             return ActivityTaskManagerService.this.startActivityAsUser(
-                    caller, callerPackage, callerFeatureId, intent,
+                    caller, callerPacakge, intent,
                     intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                     null, null, 0, Intent.FLAG_ACTIVITY_NEW_TASK, null, options, userId,
                     false /*validateIncomingUser*/);
@@ -6697,12 +6687,12 @@
 
         @Override
         public IIntentSender 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) {
+                int callingUid, int userId, IBinder token, String resultWho,
+                int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
+                Bundle bOptions) {
             synchronized (mGlobalLock) {
-                return getIntentSenderLocked(type, packageName, featureId, callingUid, userId,
-                        token, resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
+                return getIntentSenderLocked(type, packageName, 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 8fa8119..16a7564 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, String callingFeatureId,
+    public int startActivity(IBinder whoThread, String callingPackage,
             Intent intent, String resolvedType, Bundle bOptions) {
         checkCaller();
         mService.assertPackageMatchesCallingUid(callingPackage);
@@ -148,7 +148,6 @@
         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/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
index 06e7b48..9e93e14 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -18,6 +18,9 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 
+import android.content.res.Resources;
+import android.text.TextUtils;
+
 import com.android.server.wm.DisplayContent.TaskContainers;
 
 /**
@@ -42,7 +45,18 @@
      */
     protected final TaskContainers mTaskContainers;
 
-    DisplayAreaPolicy(WindowManagerService wmService,
+    /**
+     * Construct a new {@link DisplayAreaPolicy}
+     *
+     * @param wmService the window manager service instance
+     * @param content the display content for which the policy applies
+     * @param root the root display area under which the policy operates
+     * @param imeContainer the ime container that the policy must attach
+     * @param taskContainers the task container that the policy must attach
+     *
+     * @see #attachDisplayAreas()
+     */
+    protected DisplayAreaPolicy(WindowManagerService wmService,
             DisplayContent content, DisplayArea.Root root,
             DisplayArea<? extends WindowContainer> imeContainer, TaskContainers taskContainers) {
         mWmService = wmService;
@@ -119,5 +133,55 @@
                     throw new IllegalArgumentException("don't know how to sort " + token);
             }
         }
+
+        /** Provider for {@link DisplayAreaPolicy.Default platform-default display area policy}. */
+        static class Provider implements DisplayAreaPolicy.Provider {
+            @Override
+            public DisplayAreaPolicy instantiate(WindowManagerService wmService,
+                    DisplayContent content, DisplayArea.Root root,
+                    DisplayArea<? extends WindowContainer> imeContainer,
+                    TaskContainers taskContainers) {
+                return new DisplayAreaPolicy.Default(wmService, content, root, imeContainer,
+                        taskContainers);
+            }
+        }
+    }
+
+    /**
+     * Provider for {@link DisplayAreaPolicy} instances.
+     *
+     * By implementing this interface and overriding the
+     * {@code config_deviceSpecificDisplayAreaPolicyProvider}, a device-specific implementations
+     * of {@link DisplayAreaPolicy} can be supplied.
+     */
+    public interface Provider {
+        /**
+         * Instantiate a new DisplayAreaPolicy.
+         *
+         * @see DisplayAreaPolicy#DisplayAreaPolicy
+         */
+        DisplayAreaPolicy instantiate(WindowManagerService wmService,
+                DisplayContent content, DisplayArea.Root root,
+                DisplayArea<? extends WindowContainer> imeContainer,
+                TaskContainers taskContainers);
+
+        /**
+         * Instantiate the device-specific {@link Provider}.
+         */
+        static Provider fromResources(Resources res) {
+            String name = res.getString(
+                    com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider);
+            if (TextUtils.isEmpty(name)) {
+                return new DisplayAreaPolicy.Default.Provider();
+            }
+            try {
+                return (Provider) Class.forName(name).newInstance();
+            } catch (ReflectiveOperationException | ClassCastException e) {
+                throw new IllegalStateException("Couldn't instantiate class " + name
+                        + " for config_deviceSpecificDisplayAreaPolicyProvider:"
+                        + " make sure it has a public zero-argument constructor"
+                        + " and implements DisplayAreaPolicy.Provider", e);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 810aa34..3b658c0 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -304,8 +304,7 @@
 
     private final DisplayArea.Root mRootDisplayArea = new DisplayArea.Root(mWmService);
 
-    private final DisplayAreaPolicy mDisplayAreaPolicy = new DisplayAreaPolicy.Default(
-            mWmService, this, mRootDisplayArea, mImeWindowsContainers, mTaskContainers);
+    private final DisplayAreaPolicy mDisplayAreaPolicy;
 
     private WindowState mTmpWindow;
     private WindowState mTmpWindow2;
@@ -1027,6 +1026,8 @@
         super.addChild(mWindowContainers, null);
         super.addChild(mOverlayContainers, null);
 
+        mDisplayAreaPolicy = mWmService.mDisplayAreaPolicyProvider.instantiate(
+                mWmService, this, mRootDisplayArea, mImeWindowsContainers, mTaskContainers);
         mWindowContainers.addChildren();
 
         // Sets the display content for the children.
@@ -5659,7 +5660,8 @@
         return activityType == ACTIVITY_TYPE_STANDARD
                 && (windowingMode == WINDOWING_MODE_FULLSCREEN
                 || windowingMode == WINDOWING_MODE_FREEFORM
-                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+                || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                || windowingMode == WINDOWING_MODE_MULTI_WINDOW);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index d0179ad..51b9916 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -44,6 +44,7 @@
 import android.view.WindowInsetsAnimationCallback;
 import android.view.WindowInsetsAnimationControlListener;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.DisplayThread;
 
 /**
@@ -107,11 +108,11 @@
             changed = true;
         }
         if (changed) {
-            startAnimation(mShowingTransientTypes, true, () -> {
+            mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(),
+                    mShowingTransientTypes.toArray());
+            updateBarControlTarget(mFocusedWin);
+            startAnimation(true /* show */, () -> {
                 synchronized (mDisplayContent.mWmService.mGlobalLock) {
-                    mPolicy.getStatusBarManagerInternal().showTransient(
-                            mDisplayContent.getDisplayId(),
-                            mShowingTransientTypes.toArray());
                     mStateController.notifyInsetsChanged();
                 }
             });
@@ -122,7 +123,7 @@
         if (mShowingTransientTypes.size() == 0) {
             return;
         }
-        startAnimation(mShowingTransientTypes, false, () -> {
+        startAnimation(false /* show */, () -> {
             synchronized (mDisplayContent.mWmService.mGlobalLock) {
                 mShowingTransientTypes.clear();
                 mStateController.notifyInsetsChanged();
@@ -268,18 +269,20 @@
         return isDockedStackVisible || isFreeformStackVisible || isResizing;
     }
 
-    private void startAnimation(IntArray internalTypes, boolean show, Runnable callback) {
+    @VisibleForTesting
+    void startAnimation(boolean show, Runnable callback) {
         int typesReady = 0;
         final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
-        updateBarControlTarget(mFocusedWin);
-        for (int i = internalTypes.size() - 1; i >= 0; i--) {
+        final IntArray showingTransientTypes = mShowingTransientTypes;
+        for (int i = showingTransientTypes.size() - 1; i >= 0; i--) {
             InsetsSourceProvider provider =
-                    mStateController.getSourceProvider(internalTypes.get(i));
-            if (provider == null) continue;
-            InsetsSourceControl control = provider.getControl(provider.getControlTarget());
-            if (control == null || control.getLeash() == null) continue;
-            typesReady |= InsetsState.toPublicType(internalTypes.get(i));
-            controls.put(control.getType(), control);
+                    mStateController.getSourceProvider(showingTransientTypes.get(i));
+            InsetsSourceControl control = provider.getControl(mTransientControlTarget);
+            if (control == null || control.getLeash() == null) {
+                continue;
+            }
+            typesReady |= InsetsState.toPublicType(showingTransientTypes.get(i));
+            controls.put(control.getType(), new InsetsSourceControl(control));
         }
         controlAnimationUnchecked(typesReady, controls, show, callback);
     }
@@ -335,7 +338,6 @@
             private InsetsPolicyAnimationControlListener mListener;
 
             InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) {
-                super();
                 mListener = listener;
             }
 
@@ -353,9 +355,11 @@
                         InsetsController.INTERPOLATOR, true,
                         show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
                                 : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
+                SurfaceAnimationThread.getHandler().post(
+                        () -> mListener.onReady(mAnimationControl, typesReady));
             }
 
-            /** Called on SurfaceAnimationThread lock without global WM lock held. */
+            /** Called on SurfaceAnimationThread without global WM lock held. */
             @Override
             public void scheduleApplyChangeInsets() {
                 InsetsState state = getState();
@@ -384,7 +388,7 @@
                 return overrideState;
             }
 
-            /** Called on SurfaceAnimationThread lock without global WM lock held. */
+            /** Called on SurfaceAnimationThread without global WM lock held. */
             @Override
             public void applySurfaceParams(
                     final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
@@ -396,14 +400,12 @@
                 t.apply();
             }
 
-            /** Called on SurfaceAnimationThread lock without global WM lock held. */
             @Override
             public void startAnimation(InsetsAnimationControlImpl controller,
                     WindowInsetsAnimationControlListener listener, int types,
                     WindowInsetsAnimationCallback.InsetsAnimation animation,
                     WindowInsetsAnimationCallback.AnimationBounds bounds,
                     int layoutDuringAnimation) {
-                SurfaceAnimationThread.getHandler().post(() -> listener.onReady(controller, types));
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 7986659..0d3f6b9 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -59,6 +59,7 @@
     private final InsetsSourceControl mFakeControl;
     private @Nullable InsetsSourceControl mControl;
     private @Nullable InsetsControlTarget mControlTarget;
+    private @Nullable InsetsControlTarget mPendingControlTarget;
     private @Nullable InsetsControlTarget mFakeControlTarget;
 
     private @Nullable ControlAdapter mAdapter;
@@ -140,8 +141,9 @@
             mSource.setVisibleFrame(null);
         } else if (mControllable) {
             mWin.setControllableInsetProvider(this);
-            if (mControlTarget != null) {
-                updateControlForTarget(mControlTarget, true /* force */);
+            if (mPendingControlTarget != null) {
+                updateControlForTarget(mPendingControlTarget, true /* force */);
+                mPendingControlTarget = null;
             }
         }
     }
@@ -245,7 +247,7 @@
             setWindow(null, null, null);
         }
         if (mWin == null) {
-            mControlTarget = target;
+            mPendingControlTarget = target;
             return;
         }
         if (target == mControlTarget && !force) {
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index caaa430..2d7d3f1 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
@@ -89,6 +90,12 @@
         if (type == ITYPE_NAVIGATION_BAR) {
             state.removeSource(ITYPE_IME);
             state.removeSource(ITYPE_STATUS_BAR);
+            state.removeSource(ITYPE_CAPTION_BAR);
+        }
+
+        // Status bar doesn't get influenced by caption bar
+        if (type == ITYPE_STATUS_BAR) {
+            state.removeSource(ITYPE_CAPTION_BAR);
         }
 
         // IME needs different frames for certain cases (e.g. navigation bar in gesture nav).
@@ -212,18 +219,18 @@
     /**
      * Called when the focused window that is able to control the system bars changes.
      *
-     * @param topControlling The target that is now able to control the top bar appearance
-     *                       and visibility.
+     * @param statusControlling The target that is now able to control the status bar appearance
+     *                          and visibility.
      * @param navControlling The target that is now able to control the nav bar appearance
      *                       and visibility.
      */
-    void onBarControlTargetChanged(@Nullable InsetsControlTarget topControlling,
-            @Nullable InsetsControlTarget fakeTopControlling,
+    void onBarControlTargetChanged(@Nullable InsetsControlTarget statusControlling,
+            @Nullable InsetsControlTarget fakeStatusControlling,
             @Nullable InsetsControlTarget navControlling,
             @Nullable InsetsControlTarget fakeNavControlling) {
-        onControlChanged(ITYPE_STATUS_BAR, topControlling);
+        onControlChanged(ITYPE_STATUS_BAR, statusControlling);
         onControlChanged(ITYPE_NAVIGATION_BAR, navControlling);
-        onControlFakeTargetChanged(ITYPE_STATUS_BAR, fakeTopControlling);
+        onControlFakeTargetChanged(ITYPE_STATUS_BAR, fakeStatusControlling);
         onControlFakeTargetChanged(ITYPE_NAVIGATION_BAR, fakeNavControlling);
         notifyPendingInsetsControlChanged();
     }
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 3771b3e..a9dc36d 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -42,7 +42,6 @@
 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;
@@ -156,7 +155,6 @@
      */
     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.
@@ -420,13 +418,6 @@
     }
 
     /**
-     * @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 b0492be..9770947 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -63,7 +63,6 @@
     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;
@@ -81,8 +80,8 @@
 
     RecentsAnimation(ActivityTaskManagerService atm, ActivityStackSupervisor stackSupervisor,
             ActivityStartController activityStartController, WindowManagerService wm,
-            Intent targetIntent, ComponentName recentsComponent, @Nullable String recentsFeatureId,
-            int recentsUid, @Nullable WindowProcessController caller) {
+            Intent targetIntent, ComponentName recentsComponent, int recentsUid,
+            @Nullable WindowProcessController caller) {
         mService = atm;
         mStackSupervisor = stackSupervisor;
         mDefaultDisplay = mService.mRootWindowContainer.getDefaultDisplay();
@@ -90,7 +89,6 @@
         mWindowManager = wm;
         mTargetIntent = targetIntent;
         mRecentsComponent = recentsComponent;
-        mRecentsFeatureId = recentsFeatureId;
         mRecentsUid = recentsUid;
         mCaller = caller;
         mUserId = atm.getCurrentUserId();
@@ -458,7 +456,6 @@
                 .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/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2fdcbc90..2196d89 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3323,6 +3323,14 @@
      * @return {@code true} if the top activity looks like it belongs to {@param userId}.
      */
     private void taskTopActivityIsUser(Task task, @UserIdInt int userId) {
+        // TODO(b/80414790): having utilities to loop for all leaf tasks from caller vs. checking
+        //  leaf tasks here.
+        if (!task.isLeafTask()) {
+            // No op if not a leaf task since we don't want to report root tasks to
+            // TaskStackListeners.
+            return;
+        }
+
         // To handle the case that work app is in the task but just is not the top one.
         final ActivityRecord activityRecord = task.getTopNonFinishingActivity();
         final ActivityRecord resultTo = (activityRecord != null ? activityRecord.resultTo : null);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 0e500f9..326335e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -99,6 +99,7 @@
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.AppGlobals;
+import android.app.PictureInPictureParams;
 import android.app.TaskInfo;
 import android.app.WindowConfiguration;
 import android.content.ComponentName;
@@ -183,7 +184,6 @@
     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,7 +297,6 @@
     // 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();
@@ -463,6 +462,11 @@
      */
     ITaskOrganizer mTaskOrganizer;
 
+    /**
+     * Last Picture-in-Picture params applicable to the task. Updated when the app
+     * enters Picture-in-Picture or when setPictureInPictureParams is called.
+     */
+    PictureInPictureParams mPictureInPictureParams = new PictureInPictureParams.Builder().build();
 
     /**
      * Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int,
@@ -479,8 +483,8 @@
                 true /*neverRelinquishIdentity*/,
                 _taskDescription != null ? _taskDescription : new TaskDescription(),
                 _taskId, INVALID_TASK_ID, INVALID_TASK_ID, 0 /*taskAffiliationColor*/,
-                info.applicationInfo.uid, info.packageName, null /* default featureId */,
-                info.resizeMode, info.supportsPictureInPicture(), false /*_realActivitySuspended*/,
+                info.applicationInfo.uid, info.packageName, info.resizeMode,
+                info.supportsPictureInPicture(), false /*_realActivitySuspended*/,
                 false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE, info,
                 _voiceSession, _voiceInteractor, stack);
     }
@@ -502,17 +506,18 @@
     }
 
     /** 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,
-            @Nullable String callingFeatureId, int resizeMode, boolean supportsPictureInPicture,
-            boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight,
-            ActivityInfo info, IVoiceInteractionSession _voiceSession,
-            IVoiceInteractor _voiceInteractor, ActivityStack stack) {
+            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);
@@ -551,7 +556,6 @@
         mNextAffiliateTaskId = nextTaskId;
         mCallingUid = callingUid;
         mCallingPackage = callingPackage;
-        mCallingFeatureId = callingFeatureId;
         mResizeMode = resizeMode;
         if (info != null) {
             setIntent(_intent, info);
@@ -898,7 +902,6 @@
     void setIntent(ActivityRecord r) {
         mCallingUid = r.launchedFromUid;
         mCallingPackage = r.launchedFromPackage;
-        mCallingFeatureId = r.launchedFromFeatureId;
         setIntent(r.intent, r.info);
         setLockTaskAuth(r);
 
@@ -1354,7 +1357,6 @@
             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());
@@ -1431,18 +1433,27 @@
     }
 
     /**
-     * @return whether or not there are ONLY task overlay activities in the stack.
+     * @return whether or not there are ONLY task overlay activities in the task.
      *         If {@param includeFinishing} is set, then don't ignore finishing activities in the
      *         check. If there are no task overlay activities, this call returns false.
      */
     boolean onlyHasTaskOverlayActivities(boolean includeFinishing) {
-        if (getChildCount() == 0) {
-            return false;
+        int count = 0;
+        for (int i = getChildCount() - 1; i >= 0; i--) {
+            final ActivityRecord r = getChildAt(i).asActivityRecord();
+            if (r == null) {
+                // Has a child that is other than Activity.
+                return false;
+            }
+            if (!includeFinishing && r.finishing) {
+                continue;
+            }
+            if (!r.isTaskOverlay()) {
+                return false;
+            }
+            count++;
         }
-        if (includeFinishing) {
-            return getActivity((r) -> r.isTaskOverlay()) != null;
-        }
-        return getActivity((r) -> !r.finishing && r.isTaskOverlay()) != null;
+        return count > 0;
     }
 
     private boolean autoRemoveFromRecents() {
@@ -2369,6 +2380,15 @@
         return getRootTask() == this;
     }
 
+    boolean isLeafTask() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            if (mChildren.get(i).asTask() != null) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     int getDescendantTaskCount() {
         final int[] currentCount = {0};
         final PooledConsumer c = PooledLambda.obtainConsumer((t, count) -> { count[0]++; },
@@ -3203,6 +3223,12 @@
         //                    order changes.
         final Task top = getTopMostTask();
         info.resizeMode = top != null ? top.mResizeMode : mResizeMode;
+
+        if (mPictureInPictureParams.empty()) {
+            info.pictureInPictureParams = null;
+        } else {
+            info.pictureInPictureParams = mPictureInPictureParams;
+        }
     }
 
     /**
@@ -3235,7 +3261,6 @@
         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)) {
@@ -3445,8 +3470,6 @@
         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));
@@ -3560,17 +3583,16 @@
                 long lastTimeMoved, boolean neverRelinquishIdentity,
                 TaskDescription lastTaskDescription, int taskAffiliation, int prevTaskId,
                 int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage,
-                @Nullable String callingFeatureId, int resizeMode,
-                boolean supportsPictureInPicture, boolean realActivitySuspended,
+                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,
-                    callingFeatureId, resizeMode, supportsPictureInPicture, realActivitySuspended,
-                    userSetupComplete, minWidth, minHeight, null /*ActivityInfo*/,
-                    null /*_voiceSession*/, null /*_voiceInteractor*/, stack);
+                    resizeMode, supportsPictureInPicture, realActivitySuspended, userSetupComplete,
+                    minWidth, minHeight, null /*ActivityInfo*/, null /*_voiceSession*/,
+                    null /*_voiceInteractor*/, stack);
         }
 
         Task restoreFromXml(XmlPullParser in, ActivityStackSupervisor stackSupervisor)
@@ -3603,7 +3625,6 @@
             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;
@@ -3684,9 +3705,6 @@
                     case ATTR_CALLING_PACKAGE:
                         callingPackage = attrValue;
                         break;
-                    case ATTR_CALLING_FEATURE_ID:
-                        callingFeatureId = attrValue;
-                        break;
                     case ATTR_RESIZE_MODE:
                         resizeMode = Integer.parseInt(attrValue);
                         break;
@@ -3788,8 +3806,8 @@
                     autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
                     lastTimeOnTop, neverRelinquishIdentity, taskDescription,
                     taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
-                    callingPackage, callingFeatureId, resizeMode, supportsPictureInPicture,
-                    realActivitySuspended, userSetupComplete, minWidth, minHeight, null /*stack*/);
+                    callingPackage, resizeMode, supportsPictureInPicture, realActivitySuspended,
+                    userSetupComplete, minWidth, minHeight, null /*stack*/);
             task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
             task.setBounds(lastNonFullscreenBounds);
 
@@ -3941,4 +3959,10 @@
     void onWindowFocusChanged(boolean hasFocus) {
         updateShadowsRadius(hasFocus, getPendingTransaction());
     }
+
+    void setPictureInPictureParams(PictureInPictureParams p) {
+        mPictureInPictureParams.copyOnlySet(p);
+        mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
+                this, true /* force */);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 0a0530c9..4b13a0c 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -43,6 +43,7 @@
 import android.view.SurfaceControl;
 import android.view.WindowContainerTransaction;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledLambda;
 
@@ -379,7 +380,8 @@
     }
 
     @Override
-    public List<RunningTaskInfo> getChildTasks(IWindowContainer parent) {
+    public List<RunningTaskInfo> getChildTasks(IWindowContainer parent,
+            @Nullable int[] activityTypes) {
         enforceStackPermission("getChildTasks()");
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -405,6 +407,10 @@
                 for (int i = dc.getStackCount() - 1; i >= 0; --i) {
                     final ActivityStack as = dc.getStackAt(i);
                     if (as.getTile() == container) {
+                        if (activityTypes != null
+                                && !ArrayUtils.contains(activityTypes, as.getActivityType())) {
+                            continue;
+                        }
                         final RunningTaskInfo info = new RunningTaskInfo();
                         as.fillTaskInfo(info);
                         out.add(info);
@@ -417,6 +423,40 @@
         }
     }
 
+    @Override
+    public List<RunningTaskInfo> getRootTasks(int displayId, @Nullable int[] activityTypes) {
+        enforceStackPermission("getRootTasks()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final DisplayContent dc =
+                        mService.mRootWindowContainer.getDisplayContent(displayId);
+                if (dc == null) {
+                    throw new IllegalArgumentException("Display " + displayId + " doesn't exist");
+                }
+                ArrayList<RunningTaskInfo> out = new ArrayList<>();
+                for (int i = dc.getStackCount() - 1; i >= 0; --i) {
+                    final ActivityStack task = dc.getStackAt(i);
+                    if (task.getTile() != null) {
+                        // a tile is supposed to look like a parent, so don't include their
+                        // "children" here. They can be accessed via getChildTasks()
+                        continue;
+                    }
+                    if (activityTypes != null
+                            && !ArrayUtils.contains(activityTypes, task.getActivityType())) {
+                        continue;
+                    }
+                    final RunningTaskInfo info = new RunningTaskInfo();
+                    task.fillTaskInfo(info);
+                    out.add(info);
+                }
+                return out;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     private int sanitizeAndApplyChange(WindowContainer container,
             WindowContainerTransaction.Change change) {
         if (!(container instanceof Task)) {
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
index 369db05..add11d6 100644
--- a/services/core/java/com/android/server/wm/TaskTile.java
+++ b/services/core/java/com/android/server/wm/TaskTile.java
@@ -60,11 +60,10 @@
                 System.currentTimeMillis(), true /*neverRelinquishIdentity*/,
                 new ActivityManager.TaskDescription(), id, INVALID_TASK_ID, INVALID_TASK_ID,
                 0 /*taskAffiliationColor*/, 0 /*callingUid*/, "" /*callingPackage*/,
-                null /*callingFeatureId*/, RESIZE_MODE_RESIZEABLE,
-                false /*supportsPictureInPicture*/, false /*_realActivitySuspended*/,
-                false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE,
-                createEmptyActivityInfo(), null /*voiceSession*/, null /*voiceInteractor*/,
-                null /*stack*/);
+                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 6330985..d98c18c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -187,6 +187,7 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
@@ -309,6 +310,8 @@
         implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowManagerService" : TAG_WM;
 
+    private static final String WM_USE_BLAST_ADAPTER_FLAG = "wm_use_blast_adapter";
+
     static final int LAYOUT_REPEAT_THRESHOLD = 4;
 
     static final boolean PROFILE_ORIENTATION = false;
@@ -413,6 +416,8 @@
 
     final WindowTracing mWindowTracing;
 
+    final DisplayAreaPolicy.Provider mDisplayAreaPolicyProvider;
+
     final private KeyguardDisableHandler mKeyguardDisableHandler;
     // TODO: eventually unify all keyguard state in a common place instead of having it spread over
     // AM's KeyguardController and the policy's KeyguardServiceDelegate.
@@ -623,6 +628,9 @@
     // The root of the device window hierarchy.
     RootWindowContainer mRoot;
 
+    // Whether the system should use BLAST for ViewRootImpl
+    final boolean mUseBLAST;
+
     int mDockedStackCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
     Rect mDockedStackCreateBounds;
 
@@ -1137,6 +1145,10 @@
         mAnimator = new WindowAnimator(this);
         mRoot = new RootWindowContainer(this);
 
+        mUseBLAST = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
+                    WM_USE_BLAST_ADAPTER_FLAG, false);
+
         mWindowPlacerLocked = new WindowSurfacePlacer(this);
         mTaskSnapshotController = new TaskSnapshotController(this);
 
@@ -1255,6 +1267,10 @@
 
         LocalServices.addService(WindowManagerInternal.class, new LocalService());
         mEmbeddedWindowController = new EmbeddedWindowController(mGlobalLock);
+
+        mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
+                mContext.getResources());
+
         setGlobalShadowSettings();
     }
 
@@ -5051,6 +5067,11 @@
     }
 
     @Override
+    public boolean useBLAST() {
+        return mUseBLAST;
+    }
+
+    @Override
     public void getInitialDisplaySize(int displayId, Point size) {
         synchronized (mGlobalLock) {
             final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
@@ -8021,7 +8042,11 @@
             int displayId, Rect outContentInsets, Rect outStableInsets,
             DisplayCutout.ParcelableWrapper displayCutout) {
         synchronized (mGlobalLock) {
-            final DisplayContent dc = mRoot.getDisplayContent(displayId);
+            final DisplayContent dc = mRoot.getDisplayContentOrCreate(displayId);
+            if (dc == null) {
+                throw new WindowManager.InvalidDisplayException("Display#" + displayId
+                        + "could not be found!");
+            }
             final WindowToken windowToken = dc.getWindowToken(attrs.token);
             final ActivityRecord activity;
             if (windowToken != null && windowToken.asActivityRecord() != null) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 553ec42..cb687c9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -458,8 +458,6 @@
     // A collection of user restrictions that are deprecated and should simply be ignored.
     private static final Set<String> DEPRECATED_USER_RESTRICTIONS;
     private static final String AB_DEVICE_KEY = "ro.build.ab_update";
-    // Permissions related to location which must not be granted automatically
-    private static  final Set<String> LOCATION_PERMISSIONS;
 
     static {
         SECURE_SETTINGS_WHITELIST = new ArraySet<>();
@@ -504,11 +502,6 @@
         DEPRECATED_USER_RESTRICTIONS = Sets.newHashSet(
                 UserManager.DISALLOW_ADD_MANAGED_PROFILE,
                 UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
-
-        LOCATION_PERMISSIONS = Sets.newHashSet(
-                permission.ACCESS_FINE_LOCATION,
-                permission.ACCESS_BACKGROUND_LOCATION,
-                permission.ACCESS_COARSE_LOCATION);
     }
 
     /**
@@ -560,6 +553,16 @@
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
     private static final long ADMIN_APP_PASSWORD_COMPLEXITY = 123562444L;
 
+    /**
+     * Admin apps targeting Android R+ may not use
+     * {@link android.app.admin.DevicePolicyManager#setSecureSetting} to change the deprecated
+     * {@link android.provider.Settings.Secure#LOCATION_MODE} setting. Instead they should use
+     * {@link android.app.admin.DevicePolicyManager#setLocationEnabled}.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    private static final long USE_SET_LOCATION_ENABLED = 117835097L;
+
     final Context mContext;
     final Injector mInjector;
     final IPackageManager mIPackageManager;
@@ -8520,6 +8523,9 @@
     }
 
     private void clearOverrideApnUnchecked() {
+        if (!mHasTelephonyFeature) {
+            return;
+        }
         // Disable Override APNs and remove them from database.
         setOverrideApnsEnabledUnchecked(false);
         final List<ApnSetting> apns = getOverrideApnsUnchecked();
@@ -11558,13 +11564,22 @@
 
     @Override
     public void setLocationEnabled(ComponentName who, boolean locationEnabled) {
-        Objects.requireNonNull(who, "ComponentName is null");
-        enforceDeviceOwner(who);
+        enforceDeviceOwner(Objects.requireNonNull(who));
 
-        UserHandle userHandle = mInjector.binderGetCallingUserHandle();
-        mInjector.binderWithCleanCallingIdentity(
-                () -> mInjector.getLocationManager().setLocationEnabledForUser(locationEnabled,
-                        userHandle));
+        UserHandle user = mInjector.binderGetCallingUserHandle();
+
+        mInjector.binderWithCleanCallingIdentity(() -> {
+            boolean wasLocationEnabled = mInjector.getLocationManager().isLocationEnabledForUser(
+                    user);
+            mInjector.getLocationManager().setLocationEnabledForUser(locationEnabled, user);
+
+            // make a best effort to only show the notification if the admin is actually changing
+            // something. this is subject to race conditions with settings changes, but those are
+            // unlikely to realistically interfere
+            if (wasLocationEnabled != locationEnabled) {
+                showLocationSettingsChangedNotification(user);
+            }
+        });
 
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_SECURE_SETTING)
@@ -11575,6 +11590,25 @@
                 .write();
     }
 
+    private void showLocationSettingsChangedNotification(UserHandle user) {
+        PendingIntent locationSettingsIntent = mInjector.pendingIntentGetActivityAsUser(mContext, 0,
+                new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
+                        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK), PendingIntent.FLAG_UPDATE_CURRENT,
+                null, user);
+        Notification notification = new Notification.Builder(mContext,
+                SystemNotificationChannels.DEVICE_ADMIN)
+                .setSmallIcon(R.drawable.ic_info_outline)
+                .setContentTitle(mContext.getString(R.string.location_changed_notification_title))
+                .setContentText(mContext.getString(R.string.location_changed_notification_text))
+                .setColor(mContext.getColor(R.color.system_notification_accent_color))
+                .setShowWhen(true)
+                .setContentIntent(locationSettingsIntent)
+                .setAutoCancel(true)
+                .build();
+        mInjector.getNotificationManager().notify(SystemMessage.NOTE_LOCATION_CHANGED,
+                notification);
+    }
+
     @Override
     public void requestSetLocationProviderAllowed(ComponentName who, String provider,
             boolean providerAllowed) {
@@ -11645,6 +11679,12 @@
                 throw new SecurityException(String.format(
                         "Permission denial: Profile owners cannot update %1$s", setting));
             }
+            if (setting.equals(Settings.Secure.LOCATION_MODE)
+                    && isSetSecureSettingLocationModeCheckEnabled(who.getPackageName(),
+                    callingUserId)) {
+                throw new UnsupportedOperationException(Settings.Secure.LOCATION_MODE + " is "
+                        + "deprecated. Please use setLocationEnabled() instead.");
+            }
             if (setting.equals(Settings.Secure.INSTALL_NON_MARKET_APPS)) {
                 if (getTargetSdk(who.getPackageName(), callingUserId) >= Build.VERSION_CODES.O) {
                     throw new UnsupportedOperationException(Settings.Secure.INSTALL_NON_MARKET_APPS
@@ -11689,6 +11729,9 @@
                     saveSettingsLocked(callingUserId);
                 }
                 mInjector.settingsSecurePutStringForUser(setting, value, callingUserId);
+                if (setting.equals(Settings.Secure.LOCATION_MODE)) {
+                    showLocationSettingsChangedNotification(UserHandle.of(callingUserId));
+                }
             });
         }
         DevicePolicyEventLogger
@@ -11698,6 +11741,16 @@
                 .write();
     }
 
+    private boolean isSetSecureSettingLocationModeCheckEnabled(String packageName, int userId) {
+        try {
+            return mIPlatformCompat.isChangeEnabledByPackageName(USE_SET_LOCATION_ENABLED,
+                    packageName, userId);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e);
+            return getTargetSdk(packageName, userId) > Build.VERSION_CODES.Q;
+        }
+    }
+
     @Override
     public void setMasterVolumeMuted(ComponentName who, boolean on) {
         Objects.requireNonNull(who, "ComponentName is null");
@@ -12541,14 +12594,6 @@
                             true);
                 }
 
-                // Prevent granting location-related permissions without user consent.
-                if (LOCATION_PERMISSIONS.contains(permission)
-                        && grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED
-                        && !isUnattendedManagedKioskUnchecked()) {
-                    callback.sendResult(null);
-                    return;
-                }
-
                 if (grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED
                         || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
                         || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) {
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 3b51377..8012e74 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1180,7 +1180,7 @@
         }
 
         // Create new lib file without signature info
-        incfs::NewFileParams libFileParams;
+        incfs::NewFileParams libFileParams{};
         libFileParams.size = uncompressedLen;
         libFileParams.verification.hashAlgorithm = INCFS_HASH_NONE;
         // Metadata of the new lib file is its relative path
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 2444dde..2e7ced3 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -122,7 +122,6 @@
     }
 
     RawMetadata getMetadata(StorageId storage, FileId node) const;
-    std::string getSignatureData(StorageId storage, FileId node) const;
 
     std::vector<std::string> listFiles(StorageId storage) const;
     bool startLoading(StorageId storage) const;
diff --git a/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java b/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java
new file mode 100644
index 0000000..203e980
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/AbstractProtoDiskReadWriter.java
@@ -0,0 +1,264 @@
+/*
+ * 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.people.data;
+
+import android.annotation.MainThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Base class for reading and writing protobufs on disk from a root directory. Callers should
+ * ensure that the root directory is unlocked before doing I/O operations using this class.
+ *
+ * @param <T> is the data class representation of a protobuf.
+ */
+abstract class AbstractProtoDiskReadWriter<T> {
+
+    private static final String TAG = AbstractProtoDiskReadWriter.class.getSimpleName();
+    private static final long SHUTDOWN_DISK_WRITE_TIMEOUT = 5L * DateUtils.SECOND_IN_MILLIS;
+
+    private final File mRootDir;
+    private final ScheduledExecutorService mScheduledExecutorService;
+    private final long mWriteDelayMs;
+
+    @GuardedBy("this")
+    private ScheduledFuture<?> mScheduledFuture;
+
+    @GuardedBy("this")
+    private Map<String, T> mScheduledFileDataMap = new ArrayMap<>();
+
+    /**
+     * Child class shall provide a {@link ProtoStreamWriter} to facilitate the writing of data as a
+     * protobuf on disk.
+     */
+    abstract ProtoStreamWriter<T> protoStreamWriter();
+
+    /**
+     * Child class shall provide a {@link ProtoStreamReader} to facilitate the reading of protobuf
+     * data on disk.
+     */
+    abstract ProtoStreamReader<T> protoStreamReader();
+
+    AbstractProtoDiskReadWriter(@NonNull File rootDir, long writeDelayMs,
+            @NonNull ScheduledExecutorService scheduledExecutorService) {
+        mRootDir = rootDir;
+        mWriteDelayMs = writeDelayMs;
+        mScheduledExecutorService = scheduledExecutorService;
+    }
+
+    @WorkerThread
+    void delete(@NonNull String fileName) {
+        final File file = getFile(fileName);
+        if (!file.exists()) {
+            return;
+        }
+        if (!file.delete()) {
+            Slog.e(TAG, "Failed to delete file: " + file.getPath());
+        }
+    }
+
+    @WorkerThread
+    void writeTo(@NonNull String fileName, @NonNull T data) {
+        final File file = getFile(fileName);
+        final AtomicFile atomicFile = new AtomicFile(file);
+
+        FileOutputStream fileOutputStream = null;
+        try {
+            fileOutputStream = atomicFile.startWrite();
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to write to protobuf file.", e);
+            return;
+        }
+
+        try {
+            final ProtoOutputStream protoOutputStream = new ProtoOutputStream(fileOutputStream);
+            protoStreamWriter().write(protoOutputStream, data);
+            protoOutputStream.flush();
+            atomicFile.finishWrite(fileOutputStream);
+            fileOutputStream = null;
+        } finally {
+            // When fileInputStream is null (successful write), this will no-op.
+            atomicFile.failWrite(fileOutputStream);
+        }
+    }
+
+    @WorkerThread
+    @Nullable
+    T read(@NonNull String fileName) {
+        File[] files = mRootDir.listFiles(
+                pathname -> pathname.isFile() && pathname.getName().equals(fileName));
+        if (files == null || files.length == 0) {
+            return null;
+        } else if (files.length > 1) {
+            // This can't possibly happen, but sanity check.
+            Slog.w(TAG, "Found multiple files with the same name: " + Arrays.toString(files));
+        }
+        return parseFile(files[0]);
+    }
+
+    /**
+     * Reads all files in directory and returns a map with file names as keys and parsed file
+     * contents as values.
+     */
+    @WorkerThread
+    @Nullable
+    Map<String, T> readAll() {
+        File[] files = mRootDir.listFiles(File::isFile);
+        if (files == null) {
+            return null;
+        }
+
+        Map<String, T> results = new ArrayMap<>();
+        for (File file : files) {
+            T result = parseFile(file);
+            if (result != null) {
+                results.put(file.getName(), result);
+            }
+        }
+        return results;
+    }
+
+    /**
+     * Schedules the specified data to be flushed to a file in the future. Subsequent
+     * calls for the same file before the flush occurs will replace the previous data but will not
+     * reset when the flush will occur. All unique files will be flushed at the same time.
+     */
+    @MainThread
+    synchronized void scheduleSave(@NonNull String fileName, @NonNull T data) {
+        mScheduledFileDataMap.put(fileName, data);
+
+        if (mScheduledExecutorService.isShutdown()) {
+            Slog.e(TAG, "Worker is shutdown, failed to schedule data saving.");
+            return;
+        }
+
+        // Skip scheduling another flush when one is pending.
+        if (mScheduledFuture != null) {
+            return;
+        }
+
+        mScheduledFuture = mScheduledExecutorService.schedule(this::flushScheduledData,
+                mWriteDelayMs, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * Saves specified data immediately on a background thread, and blocks until its completed. This
+     * is useful for when device is powering off.
+     */
+    @MainThread
+    synchronized void saveImmediately(@NonNull String fileName, @NonNull T data) {
+        if (mScheduledExecutorService.isShutdown()) {
+            return;
+        }
+        // Cancel existing future.
+        if (mScheduledFuture != null) {
+
+            // We shouldn't need to interrupt as this method and threaded task
+            // #flushScheduledData are both synchronized.
+            mScheduledFuture.cancel(true);
+        }
+
+        mScheduledFileDataMap.put(fileName, data);
+        // Submit flush and blocks until it completes. Blocking will prevent the device from
+        // shutting down before flushing completes.
+        Future<?> future = mScheduledExecutorService.submit(this::flushScheduledData);
+        try {
+            future.get(SHUTDOWN_DISK_WRITE_TIMEOUT, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException | ExecutionException | TimeoutException e) {
+            Slog.e(TAG, "Failed to save data immediately.", e);
+        }
+    }
+
+    @WorkerThread
+    private synchronized void flushScheduledData() {
+        if (mScheduledFileDataMap.isEmpty()) {
+            mScheduledFuture = null;
+            return;
+        }
+        for (String fileName : mScheduledFileDataMap.keySet()) {
+            T data = mScheduledFileDataMap.remove(fileName);
+            writeTo(fileName, data);
+        }
+        mScheduledFuture = null;
+    }
+
+    @WorkerThread
+    @Nullable
+    private T parseFile(@NonNull File file) {
+        final AtomicFile atomicFile = new AtomicFile(file);
+        try (FileInputStream fileInputStream = atomicFile.openRead()) {
+            final ProtoInputStream protoInputStream = new ProtoInputStream(fileInputStream);
+            return protoStreamReader().read(protoInputStream);
+        } catch (IOException e) {
+            Slog.e(TAG, "Failed to parse protobuf file.", e);
+        }
+        return null;
+    }
+
+    @NonNull
+    private File getFile(String fileName) {
+        return new File(mRootDir, fileName);
+    }
+
+    /**
+     * {@code ProtoStreamWriter} writes {@code T} fields to {@link ProtoOutputStream}.
+     *
+     * @param <T> is the data class representation of a protobuf.
+     */
+    interface ProtoStreamWriter<T> {
+
+        /**
+         * Writes {@code T} to {@link ProtoOutputStream}.
+         */
+        void write(@NonNull ProtoOutputStream protoOutputStream, @NonNull T data);
+    }
+
+    /**
+     * {@code ProtoStreamReader} reads {@link ProtoInputStream} and translate it to {@code T}.
+     *
+     * @param <T> is the data class representation of a protobuf.
+     */
+    interface ProtoStreamReader<T> {
+        /**
+         * Reads {@link ProtoInputStream} and translates it to {@code T}.
+         */
+        @Nullable
+        T read(@NonNull ProtoInputStream protoInputStream);
+    }
+}
diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java
index b60ed3e..ce35366 100644
--- a/services/people/java/com/android/server/people/data/ConversationInfo.java
+++ b/services/people/java/com/android/server/people/data/ConversationInfo.java
@@ -20,12 +20,18 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.LocusId;
+import android.content.LocusIdProto;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutInfo.ShortcutFlags;
 import android.net.Uri;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.util.Preconditions;
+import com.android.server.people.ConversationInfoProto;
 
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
@@ -35,6 +41,8 @@
  */
 public class ConversationInfo {
 
+    private static final String TAG = ConversationInfo.class.getSimpleName();
+
     private static final int FLAG_IMPORTANT = 1;
 
     private static final int FLAG_NOTIFICATION_SILENCED = 1 << 1;
@@ -241,6 +249,72 @@
         return (mConversationFlags & flags) == flags;
     }
 
+    /** Writes field members to {@link ProtoOutputStream}. */
+    void writeToProto(@NonNull ProtoOutputStream protoOutputStream) {
+        protoOutputStream.write(ConversationInfoProto.SHORTCUT_ID, mShortcutId);
+        if (mLocusId != null) {
+            long locusIdToken = protoOutputStream.start(ConversationInfoProto.LOCUS_ID_PROTO);
+            protoOutputStream.write(LocusIdProto.LOCUS_ID, mLocusId.getId());
+            protoOutputStream.end(locusIdToken);
+        }
+        if (mContactUri != null) {
+            protoOutputStream.write(ConversationInfoProto.CONTACT_URI, mContactUri.toString());
+        }
+        if (mNotificationChannelId != null) {
+            protoOutputStream.write(ConversationInfoProto.NOTIFICATION_CHANNEL_ID,
+                    mNotificationChannelId);
+        }
+        protoOutputStream.write(ConversationInfoProto.SHORTCUT_FLAGS, mShortcutFlags);
+        protoOutputStream.write(ConversationInfoProto.CONVERSATION_FLAGS, mConversationFlags);
+    }
+
+    /** Reads from {@link ProtoInputStream} and constructs a {@link ConversationInfo}. */
+    @NonNull
+    static ConversationInfo readFromProto(@NonNull ProtoInputStream protoInputStream)
+            throws IOException {
+        ConversationInfo.Builder builder = new ConversationInfo.Builder();
+        while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+            switch (protoInputStream.getFieldNumber()) {
+                case (int) ConversationInfoProto.SHORTCUT_ID:
+                    builder.setShortcutId(
+                            protoInputStream.readString(ConversationInfoProto.SHORTCUT_ID));
+                    break;
+                case (int) ConversationInfoProto.LOCUS_ID_PROTO:
+                    long locusIdToken = protoInputStream.start(
+                            ConversationInfoProto.LOCUS_ID_PROTO);
+                    while (protoInputStream.nextField()
+                            != ProtoInputStream.NO_MORE_FIELDS) {
+                        if (protoInputStream.getFieldNumber() == (int) LocusIdProto.LOCUS_ID) {
+                            builder.setLocusId(new LocusId(
+                                    protoInputStream.readString(LocusIdProto.LOCUS_ID)));
+                        }
+                    }
+                    protoInputStream.end(locusIdToken);
+                    break;
+                case (int) ConversationInfoProto.CONTACT_URI:
+                    builder.setContactUri(Uri.parse(protoInputStream.readString(
+                            ConversationInfoProto.CONTACT_URI)));
+                    break;
+                case (int) ConversationInfoProto.NOTIFICATION_CHANNEL_ID:
+                    builder.setNotificationChannelId(protoInputStream.readString(
+                            ConversationInfoProto.NOTIFICATION_CHANNEL_ID));
+                    break;
+                case (int) ConversationInfoProto.SHORTCUT_FLAGS:
+                    builder.setShortcutFlags(protoInputStream.readInt(
+                            ConversationInfoProto.SHORTCUT_FLAGS));
+                    break;
+                case (int) ConversationInfoProto.CONVERSATION_FLAGS:
+                    builder.setConversationFlags(protoInputStream.readInt(
+                            ConversationInfoProto.CONVERSATION_FLAGS));
+                    break;
+                default:
+                    Slog.w(TAG, "Could not read undefined field: "
+                            + protoInputStream.getFieldNumber());
+            }
+        }
+        return builder.build();
+    }
+
     /**
      * Builder class for {@link ConversationInfo} objects.
      */
diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java
index 3649921..ea36d38 100644
--- a/services/people/java/com/android/server/people/data/ConversationStore.java
+++ b/services/people/java/com/android/server/people/data/ConversationStore.java
@@ -16,58 +16,124 @@
 
 package com.android.server.people.data;
 
+import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.WorkerThread;
 import android.content.LocusId;
 import android.net.Uri;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
 import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.proto.ProtoInputStream;
 
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.people.ConversationInfosProto;
+
+import com.google.android.collect.Lists;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.function.Consumer;
 
-/** The store that stores and accesses the conversations data for a package. */
+/**
+ * The store that stores and accesses the conversations data for a package.
+ */
 class ConversationStore {
 
+    private static final String TAG = ConversationStore.class.getSimpleName();
+
+    private static final String CONVERSATIONS_FILE_NAME = "conversations";
+
+    private static final long DISK_WRITE_DELAY = 2L * DateUtils.MINUTE_IN_MILLIS;
+
     // Shortcut ID -> Conversation Info
+    @GuardedBy("this")
     private final Map<String, ConversationInfo> mConversationInfoMap = new ArrayMap<>();
 
     // Locus ID -> Shortcut ID
+    @GuardedBy("this")
     private final Map<LocusId, String> mLocusIdToShortcutIdMap = new ArrayMap<>();
 
     // Contact URI -> Shortcut ID
+    @GuardedBy("this")
     private final Map<Uri, String> mContactUriToShortcutIdMap = new ArrayMap<>();
 
     // Phone Number -> Shortcut ID
+    @GuardedBy("this")
     private final Map<String, String> mPhoneNumberToShortcutIdMap = new ArrayMap<>();
 
     // Notification Channel ID -> Shortcut ID
+    @GuardedBy("this")
     private final Map<String, String> mNotifChannelIdToShortcutIdMap = new ArrayMap<>();
 
-    void addOrUpdate(@NonNull ConversationInfo conversationInfo) {
-        mConversationInfoMap.put(conversationInfo.getShortcutId(), conversationInfo);
+    private final ScheduledExecutorService mScheduledExecutorService;
+    private final File mPackageDir;
+    private final ContactsQueryHelper mHelper;
 
-        LocusId locusId = conversationInfo.getLocusId();
-        if (locusId != null) {
-            mLocusIdToShortcutIdMap.put(locusId, conversationInfo.getShortcutId());
-        }
+    private ConversationInfosProtoDiskReadWriter mConversationInfosProtoDiskReadWriter;
 
-        Uri contactUri = conversationInfo.getContactUri();
-        if (contactUri != null) {
-            mContactUriToShortcutIdMap.put(contactUri, conversationInfo.getShortcutId());
-        }
+    ConversationStore(@NonNull File packageDir,
+            @NonNull ScheduledExecutorService scheduledExecutorService,
+            @NonNull ContactsQueryHelper helper) {
+        mScheduledExecutorService = scheduledExecutorService;
+        mPackageDir = packageDir;
+        mHelper = helper;
+    }
 
-        String phoneNumber = conversationInfo.getContactPhoneNumber();
-        if (phoneNumber != null) {
-            mPhoneNumberToShortcutIdMap.put(phoneNumber, conversationInfo.getShortcutId());
-        }
+    /**
+     * Loads conversations from disk to memory in a background thread. This should be called
+     * after the device powers on and the user has been unlocked.
+     */
+    @MainThread
+    void loadConversationsFromDisk() {
+        mScheduledExecutorService.submit(() -> {
+            synchronized (this) {
+                ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter =
+                        getConversationInfosProtoDiskReadWriter();
+                if (conversationInfosProtoDiskReadWriter == null) {
+                    return;
+                }
+                List<ConversationInfo> conversationsOnDisk =
+                        conversationInfosProtoDiskReadWriter.read(CONVERSATIONS_FILE_NAME);
+                if (conversationsOnDisk == null) {
+                    return;
+                }
+                for (ConversationInfo conversationInfo : conversationsOnDisk) {
+                    conversationInfo = restoreConversationPhoneNumber(conversationInfo);
+                    updateConversationsInMemory(conversationInfo);
+                }
+            }
+        });
+    }
 
-        String notifChannelId = conversationInfo.getNotificationChannelId();
-        if (notifChannelId != null) {
-            mNotifChannelIdToShortcutIdMap.put(notifChannelId, conversationInfo.getShortcutId());
+    /**
+     * Immediately flushes current conversations to disk. This should be called when device is
+     * powering off.
+     */
+    @MainThread
+    synchronized void saveConversationsToDisk() {
+        ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter =
+                getConversationInfosProtoDiskReadWriter();
+        if (conversationInfosProtoDiskReadWriter != null) {
+            conversationInfosProtoDiskReadWriter.saveConversationsImmediately(
+                    new ArrayList<>(mConversationInfoMap.values()));
         }
     }
 
-    void deleteConversation(@NonNull String shortcutId) {
+    @MainThread
+    synchronized void addOrUpdate(@NonNull ConversationInfo conversationInfo) {
+        updateConversationsInMemory(conversationInfo);
+        scheduleUpdateConversationsOnDisk();
+    }
+
+    @MainThread
+    synchronized void deleteConversation(@NonNull String shortcutId) {
         ConversationInfo conversationInfo = mConversationInfoMap.remove(shortcutId);
         if (conversationInfo == null) {
             return;
@@ -92,31 +158,32 @@
         if (notifChannelId != null) {
             mNotifChannelIdToShortcutIdMap.remove(notifChannelId);
         }
+        scheduleUpdateConversationsOnDisk();
     }
 
-    void forAllConversations(@NonNull Consumer<ConversationInfo> consumer) {
+    synchronized void forAllConversations(@NonNull Consumer<ConversationInfo> consumer) {
         for (ConversationInfo ci : mConversationInfoMap.values()) {
             consumer.accept(ci);
         }
     }
 
     @Nullable
-    ConversationInfo getConversation(@Nullable String shortcutId) {
+    synchronized ConversationInfo getConversation(@Nullable String shortcutId) {
         return shortcutId != null ? mConversationInfoMap.get(shortcutId) : null;
     }
 
     @Nullable
-    ConversationInfo getConversationByLocusId(@NonNull LocusId locusId) {
+    synchronized ConversationInfo getConversationByLocusId(@NonNull LocusId locusId) {
         return getConversation(mLocusIdToShortcutIdMap.get(locusId));
     }
 
     @Nullable
-    ConversationInfo getConversationByContactUri(@NonNull Uri contactUri) {
+    synchronized ConversationInfo getConversationByContactUri(@NonNull Uri contactUri) {
         return getConversation(mContactUriToShortcutIdMap.get(contactUri));
     }
 
     @Nullable
-    ConversationInfo getConversationByPhoneNumber(@NonNull String phoneNumber) {
+    synchronized ConversationInfo getConversationByPhoneNumber(@NonNull String phoneNumber) {
         return getConversation(mPhoneNumberToShortcutIdMap.get(phoneNumber));
     }
 
@@ -124,4 +191,140 @@
     ConversationInfo getConversationByNotificationChannelId(@NonNull String notifChannelId) {
         return getConversation(mNotifChannelIdToShortcutIdMap.get(notifChannelId));
     }
+
+    @MainThread
+    private synchronized void updateConversationsInMemory(
+            @NonNull ConversationInfo conversationInfo) {
+        mConversationInfoMap.put(conversationInfo.getShortcutId(), conversationInfo);
+
+        LocusId locusId = conversationInfo.getLocusId();
+        if (locusId != null) {
+            mLocusIdToShortcutIdMap.put(locusId, conversationInfo.getShortcutId());
+        }
+
+        Uri contactUri = conversationInfo.getContactUri();
+        if (contactUri != null) {
+            mContactUriToShortcutIdMap.put(contactUri, conversationInfo.getShortcutId());
+        }
+
+        String phoneNumber = conversationInfo.getContactPhoneNumber();
+        if (phoneNumber != null) {
+            mPhoneNumberToShortcutIdMap.put(phoneNumber, conversationInfo.getShortcutId());
+        }
+
+        String notifChannelId = conversationInfo.getNotificationChannelId();
+        if (notifChannelId != null) {
+            mNotifChannelIdToShortcutIdMap.put(notifChannelId, conversationInfo.getShortcutId());
+        }
+    }
+
+    /** Schedules a dump of all conversations onto disk, overwriting existing values. */
+    @MainThread
+    private synchronized void scheduleUpdateConversationsOnDisk() {
+        ConversationInfosProtoDiskReadWriter conversationInfosProtoDiskReadWriter =
+                getConversationInfosProtoDiskReadWriter();
+        if (conversationInfosProtoDiskReadWriter != null) {
+            conversationInfosProtoDiskReadWriter.scheduleConversationsSave(
+                    new ArrayList<>(mConversationInfoMap.values()));
+        }
+    }
+
+    @Nullable
+    private ConversationInfosProtoDiskReadWriter getConversationInfosProtoDiskReadWriter() {
+        if (!mPackageDir.exists()) {
+            Slog.e(TAG, "Package data directory does not exist: " + mPackageDir.getAbsolutePath());
+            return null;
+        }
+        if (mConversationInfosProtoDiskReadWriter == null) {
+            mConversationInfosProtoDiskReadWriter = new ConversationInfosProtoDiskReadWriter(
+                    mPackageDir, CONVERSATIONS_FILE_NAME, DISK_WRITE_DELAY,
+                    mScheduledExecutorService);
+        }
+        return mConversationInfosProtoDiskReadWriter;
+    }
+
+    /**
+     * Conversation's phone number is not saved on disk, so it has to be fetched.
+     */
+    @WorkerThread
+    private ConversationInfo restoreConversationPhoneNumber(
+            @NonNull ConversationInfo conversationInfo) {
+        if (conversationInfo.getContactUri() != null) {
+            if (mHelper.query(conversationInfo.getContactUri().toString())) {
+                String phoneNumber = mHelper.getPhoneNumber();
+                if (!TextUtils.isEmpty(phoneNumber)) {
+                    conversationInfo = new ConversationInfo.Builder(
+                            conversationInfo).setContactPhoneNumber(
+                            phoneNumber).build();
+                }
+            }
+        }
+        return conversationInfo;
+    }
+
+    /** Reads and writes {@link ConversationInfo} on disk. */
+    static class ConversationInfosProtoDiskReadWriter extends
+            AbstractProtoDiskReadWriter<List<ConversationInfo>> {
+
+        private final String mConversationInfoFileName;
+
+        ConversationInfosProtoDiskReadWriter(@NonNull File baseDir,
+                @NonNull String conversationInfoFileName,
+                long writeDelayMs, @NonNull ScheduledExecutorService scheduledExecutorService) {
+            super(baseDir, writeDelayMs, scheduledExecutorService);
+            mConversationInfoFileName = conversationInfoFileName;
+        }
+
+        @Override
+        ProtoStreamWriter<List<ConversationInfo>> protoStreamWriter() {
+            return (protoOutputStream, data) -> {
+                for (ConversationInfo conversationInfo : data) {
+                    long token = protoOutputStream.start(ConversationInfosProto.CONVERSATION_INFOS);
+                    conversationInfo.writeToProto(protoOutputStream);
+                    protoOutputStream.end(token);
+                }
+            };
+        }
+
+        @Override
+        ProtoStreamReader<List<ConversationInfo>> protoStreamReader() {
+            return protoInputStream -> {
+                List<ConversationInfo> results = Lists.newArrayList();
+                try {
+                    while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+                        if (protoInputStream.getFieldNumber()
+                                != (int) ConversationInfosProto.CONVERSATION_INFOS) {
+                            continue;
+                        }
+                        long token = protoInputStream.start(
+                                ConversationInfosProto.CONVERSATION_INFOS);
+                        ConversationInfo conversationInfo = ConversationInfo.readFromProto(
+                                protoInputStream);
+                        protoInputStream.end(token);
+                        results.add(conversationInfo);
+                    }
+                } catch (IOException e) {
+                    Slog.e(TAG, "Failed to read protobuf input stream.", e);
+                }
+                return results;
+            };
+        }
+
+        /**
+         * Schedules a flush of the specified conversations to disk.
+         */
+        @MainThread
+        void scheduleConversationsSave(@NonNull List<ConversationInfo> conversationInfos) {
+            scheduleSave(mConversationInfoFileName, conversationInfos);
+        }
+
+        /**
+         * Saves the specified conversations immediately. This should be used when device is
+         * powering off.
+         */
+        @MainThread
+        void saveConversationsImmediately(@NonNull List<ConversationInfo> conversationInfos) {
+            saveImmediately(mConversationInfoFileName, conversationInfos);
+        }
+    }
 }
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 7a3ed53..c8673f8 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -86,6 +86,7 @@
     private final Context mContext;
     private final Injector mInjector;
     private final ScheduledExecutorService mUsageStatsQueryExecutor;
+    private final ScheduledExecutorService mDiskReadWriterExecutor;
 
     private final SparseArray<UserData> mUserDataArray = new SparseArray<>();
     private final SparseArray<BroadcastReceiver> mBroadcastReceivers = new SparseArray<>();
@@ -113,6 +114,7 @@
                 BackgroundThread.getHandler());
         mMmsSmsContentObserver = new MmsSmsContentObserver(
                 BackgroundThread.getHandler());
+        mDiskReadWriterExecutor = mInjector.createScheduledExecutor();
     }
 
     /** Initialization. Called when the system services are up running. */
@@ -122,13 +124,18 @@
         mUserManager = mContext.getSystemService(UserManager.class);
 
         mShortcutServiceInternal.addListener(new ShortcutServiceListener());
+
+        IntentFilter shutdownIntentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
+        BroadcastReceiver shutdownBroadcastReceiver = new ShutdownBroadcastReceiver();
+        mContext.registerReceiver(shutdownBroadcastReceiver, shutdownIntentFilter);
     }
 
     /** This method is called when a user is unlocked. */
     public void onUserUnlocked(int userId) {
         UserData userData = mUserDataArray.get(userId);
         if (userData == null) {
-            userData = new UserData(userId);
+            userData = new UserData(userId, mDiskReadWriterExecutor,
+                    mInjector.createContactsQueryHelper(mContext));
             mUserDataArray.put(userId, userData);
         }
         userData.setUserUnlocked();
@@ -662,6 +669,14 @@
         }
     }
 
+    private class ShutdownBroadcastReceiver extends BroadcastReceiver {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            forAllPackages(PackageData::saveToDisk);
+        }
+    }
+
     @VisibleForTesting
     static class Injector {
 
diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java
index 75b870c..f67699c 100644
--- a/services/people/java/com/android/server/people/data/PackageData.java
+++ b/services/people/java/com/android/server/people/data/PackageData.java
@@ -22,6 +22,8 @@
 import android.content.LocusId;
 import android.text.TextUtils;
 
+import java.io.File;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -43,17 +45,36 @@
 
     private final Predicate<String> mIsDefaultSmsAppPredicate;
 
+    private final File mPackageDataDir;
+
     PackageData(@NonNull String packageName, @UserIdInt int userId,
             @NonNull Predicate<String> isDefaultDialerPredicate,
-            @NonNull Predicate<String> isDefaultSmsAppPredicate) {
+            @NonNull Predicate<String> isDefaultSmsAppPredicate,
+            @NonNull ScheduledExecutorService scheduledExecutorService,
+            @NonNull File perUserPeopleDataDir,
+            @NonNull ContactsQueryHelper helper) {
         mPackageName = packageName;
         mUserId = userId;
-        mConversationStore = new ConversationStore();
+
+        mPackageDataDir = new File(perUserPeopleDataDir, mPackageName);
+        mConversationStore = new ConversationStore(mPackageDataDir, scheduledExecutorService,
+                helper);
         mEventStore = new EventStore();
         mIsDefaultDialerPredicate = isDefaultDialerPredicate;
         mIsDefaultSmsAppPredicate = isDefaultSmsAppPredicate;
     }
 
+    /** Called when user is unlocked. */
+    void loadFromDisk() {
+        mPackageDataDir.mkdirs();
+        mConversationStore.loadConversationsFromDisk();
+    }
+
+    /** Called when device is shutting down. */
+    void saveToDisk() {
+        mConversationStore.saveConversationsToDisk();
+    }
+
     @NonNull
     public String getPackageName() {
         return mPackageName;
diff --git a/services/people/java/com/android/server/people/data/UserData.java b/services/people/java/com/android/server/people/data/UserData.java
index 4e8fd16..aaa5db8 100644
--- a/services/people/java/com/android/server/people/data/UserData.java
+++ b/services/people/java/com/android/server/people/data/UserData.java
@@ -19,10 +19,13 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.os.Environment;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 
+import java.io.File;
 import java.util.Map;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.function.Consumer;
 
 /** The data associated with a user profile. */
@@ -30,6 +33,12 @@
 
     private final @UserIdInt int mUserId;
 
+    private final File mPerUserPeopleDataDir;
+
+    private final ScheduledExecutorService mScheduledExecutorService;
+
+    private final ContactsQueryHelper mHelper;
+
     private boolean mIsUnlocked;
 
     private Map<String, PackageData> mPackageDataMap = new ArrayMap<>();
@@ -40,8 +49,12 @@
     @Nullable
     private String mDefaultSmsApp;
 
-    UserData(@UserIdInt int userId) {
+    UserData(@UserIdInt int userId, @NonNull ScheduledExecutorService scheduledExecutorService,
+            ContactsQueryHelper helper) {
         mUserId = userId;
+        mPerUserPeopleDataDir = new File(Environment.getDataSystemCeDirectory(mUserId), "people");
+        mScheduledExecutorService = scheduledExecutorService;
+        mHelper = helper;
     }
 
     @UserIdInt int getUserId() {
@@ -56,6 +69,13 @@
 
     void setUserUnlocked() {
         mIsUnlocked = true;
+
+        // Ensures per user root directory for people data is present, and attempt to load
+        // data from disk.
+        mPerUserPeopleDataDir.mkdirs();
+        for (PackageData packageData : mPackageDataMap.values()) {
+            packageData.loadFromDisk();
+        }
     }
 
     void setUserStopped() {
@@ -103,7 +123,8 @@
     }
 
     private PackageData createPackageData(String packageName) {
-        return new PackageData(packageName, mUserId, this::isDefaultDialer, this::isDefaultSmsApp);
+        return new PackageData(packageName, mUserId, this::isDefaultDialer, this::isDefaultSmsApp,
+                mScheduledExecutorService, mPerUserPeopleDataDir, mHelper);
     }
 
     private boolean isDefaultDialer(String packageName) {
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
index 223a98b..8daef5f 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
@@ -67,8 +67,7 @@
             int pmToken,
             boolean isFullSystemRestore,
             @Nullable String[] filterSet,
-            OnTaskFinishedListener listener,
-            Map<String, Set<String>> excludedKeys) {
+            OnTaskFinishedListener listener) {
         mBackupManagerService = backupManagerService;
         mPackage = targetPackage;
         mIsFullSystemRestore = isFullSystemRestore;
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 e3453a0..3975f0b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
@@ -50,7 +50,6 @@
 @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")};
 
@@ -88,8 +87,8 @@
 
     private PendingIntentRecord createPendingIntentRecord(int flags) {
         return mPendingIntentController.getIntentSender(ActivityManager.INTENT_SENDER_BROADCAST,
-                TEST_PACKAGE_NAME, TEST_FEATURE_ID, TEST_CALLING_UID, 0, null, null, 0,
-                TEST_INTENTS, null, flags, null);
+                TEST_PACKAGE_NAME, TEST_CALLING_UID, 0, null, null, 0, TEST_INTENTS, null, flags,
+                null);
     }
 
     @Test
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index f990810..bf2b9be 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -74,7 +74,6 @@
         "libbacktrace",
         "libbase",
         "libbinder",
-        "libbinderthreadstate",
         "libc++",
         "libcutils",
         "liblog",
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 a871ec6..089a79b 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -178,7 +178,6 @@
                 new Intent(),
                 null /* callerApp */,
                 null  /* callerPackage */,
-                null /* callerFeatureId */,
                 0 /* callingPid */,
                 0 /* callingUid */,
                 false /* callerInstantApp */,
diff --git a/services/tests/servicestests/src/com/android/server/backup/UserBackupPreferencesTest.java b/services/tests/servicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
index d6efe35..5800aca 100644
--- a/services/tests/servicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/UserBackupPreferencesTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.backup;
 
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
 import androidx.test.InstrumentationRegistry;
@@ -33,18 +31,15 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.Arrays;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
 @Presubmit
 @RunWith(AndroidJUnit4.class)
 public class UserBackupPreferencesTest {
     private static final String EXCLUDED_PACKAGE_1 = "package1";
-    private static final String EXCLUDED_PACKAGE_2 = "package2";
     private static final List<String> EXCLUDED_KEYS_1 = Arrays.asList("key1", "key2");
-    private static final List<String> EXCLUDED_KEYS_2 = Arrays.asList("key1");
+    private static final List<String> EXCLUDED_KEYS_2 = Arrays.asList("key3");
 
     @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
 
@@ -60,27 +55,13 @@
     }
 
     @Test
-    public void testGetExcludedKeysForPackages_returnsExcludedKeys() {
+    public void testGetExcludedKeysForPackage_returnsExcludedKeys() {
         mExcludedRestoreKeysStorage.addExcludedKeys(EXCLUDED_PACKAGE_1, EXCLUDED_KEYS_1);
-        mExcludedRestoreKeysStorage.addExcludedKeys(EXCLUDED_PACKAGE_2, EXCLUDED_KEYS_2);
+        mExcludedRestoreKeysStorage.addExcludedKeys(EXCLUDED_PACKAGE_1, EXCLUDED_KEYS_2);
 
-        Map<String, Set<String>> excludedKeys =
-                mExcludedRestoreKeysStorage.getExcludedRestoreKeysForPackages(EXCLUDED_PACKAGE_1);
-        assertTrue(excludedKeys.containsKey(EXCLUDED_PACKAGE_1));
-        assertFalse(excludedKeys.containsKey(EXCLUDED_PACKAGE_2));
-        assertEquals(new HashSet<>(EXCLUDED_KEYS_1), excludedKeys.get(EXCLUDED_PACKAGE_1));
-    }
-
-    @Test
-    public void testGetExcludedKeysForPackages_withEmpty_list_returnsAllExcludedKeys() {
-        mExcludedRestoreKeysStorage.addExcludedKeys(EXCLUDED_PACKAGE_1, EXCLUDED_KEYS_1);
-        mExcludedRestoreKeysStorage.addExcludedKeys(EXCLUDED_PACKAGE_2, EXCLUDED_KEYS_2);
-
-        Map<String, Set<String>> excludedKeys =
-                mExcludedRestoreKeysStorage.getAllExcludedRestoreKeys();
-        assertTrue(excludedKeys.containsKey(EXCLUDED_PACKAGE_1));
-        assertTrue(excludedKeys.containsKey(EXCLUDED_PACKAGE_2));
-        assertEquals(new HashSet<>(EXCLUDED_KEYS_1), excludedKeys.get(EXCLUDED_PACKAGE_1));
-        assertEquals(new HashSet<>(EXCLUDED_KEYS_2), excludedKeys.get(EXCLUDED_PACKAGE_2));
+        Set<String> excludedKeys =
+                mExcludedRestoreKeysStorage.getExcludedRestoreKeysForPackage(EXCLUDED_PACKAGE_1);
+        assertTrue(excludedKeys.containsAll(EXCLUDED_KEYS_1));
+        assertTrue(excludedKeys.containsAll(EXCLUDED_KEYS_2));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java b/services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
index 3d22043..017c939 100644
--- a/services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
@@ -20,7 +20,9 @@
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
 import androidx.test.InstrumentationRegistry;
@@ -30,6 +32,8 @@
 import android.app.backup.BackupDataOutput;
 import android.platform.test.annotations.Presubmit;
 
+import com.android.server.backup.UserBackupManagerService;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -40,6 +44,7 @@
 import org.mockito.stubbing.Answer;
 
 import java.util.ArrayDeque;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -59,6 +64,7 @@
 
     @Mock private BackupDataInput mBackupDataInput;
     @Mock private BackupDataOutput mBackupDataOutput;
+    @Mock private UserBackupManagerService mBackupManagerService;
 
     private Set<String> mExcludedkeys = new HashSet<>();
     private Map<String, String> mBackupData = new HashMap<>();
@@ -99,6 +105,8 @@
                         return null;
                     }
                 });
+
+        mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService);
     }
 
     private void populateTestData() {
@@ -114,8 +122,9 @@
 
     @Test
     public void testFilterExcludedKeys() throws Exception {
-        mRestoreTask = new PerformUnifiedRestoreTask(Collections.singletonMap(
-                PACKAGE_NAME, mExcludedkeys));
+        when(mBackupManagerService.getExcludedRestoreKeys(eq(PACKAGE_NAME))).thenReturn(
+                mExcludedkeys);
+
         mRestoreTask.filterExcludedKeys(PACKAGE_NAME, mBackupDataInput, mBackupDataOutput);
 
         // Verify only the correct were written into BackupDataOutput object.
@@ -125,32 +134,49 @@
     }
 
     @Test
+    public void testGetExcludedKeysForPackage_alwaysReturnsLatestKeys() {
+        Set<String> firstExcludedKeys = new HashSet<>(Collections.singletonList(EXCLUDED_KEY_1));
+        when(mBackupManagerService.getExcludedRestoreKeys(eq(PACKAGE_NAME))).thenReturn(
+                firstExcludedKeys);
+        assertEquals(firstExcludedKeys, mRestoreTask.getExcludedKeysForPackage(PACKAGE_NAME));
+
+
+        Set<String> secondExcludedKeys = new HashSet<>(Arrays.asList(EXCLUDED_KEY_1,
+                EXCLUDED_KEY_2));
+        when(mBackupManagerService.getExcludedRestoreKeys(eq(PACKAGE_NAME))).thenReturn(
+                secondExcludedKeys);
+        assertEquals(secondExcludedKeys, mRestoreTask.getExcludedKeysForPackage(PACKAGE_NAME));
+    }
+
+    @Test
     public void testStageBackupData_stageForNonSystemPackageWithKeysToExclude() {
-        mRestoreTask = new PerformUnifiedRestoreTask(Collections.singletonMap(
-                PACKAGE_NAME, mExcludedkeys));
+        when(mBackupManagerService.getExcludedRestoreKeys(eq(NON_SYSTEM_PACKAGE_NAME))).thenReturn(
+                mExcludedkeys);
 
         assertTrue(mRestoreTask.shouldStageBackupData(NON_SYSTEM_PACKAGE_NAME));
     }
 
     @Test
     public void testStageBackupData_stageForNonSystemPackageWithNoKeysToExclude() {
-        mRestoreTask = new PerformUnifiedRestoreTask(Collections.emptyMap());
+        when(mBackupManagerService.getExcludedRestoreKeys(any())).thenReturn(
+                Collections.emptySet());
 
         assertTrue(mRestoreTask.shouldStageBackupData(NON_SYSTEM_PACKAGE_NAME));
     }
 
     @Test
     public void testStageBackupData_doNotStageForSystemPackageWithNoKeysToExclude() {
-        mRestoreTask = new PerformUnifiedRestoreTask(Collections.emptyMap());
+        when(mBackupManagerService.getExcludedRestoreKeys(any())).thenReturn(
+                Collections.emptySet());
 
         assertFalse(mRestoreTask.shouldStageBackupData(SYSTEM_PACKAGE_NAME));
     }
 
     @Test
     public void testStageBackupData_stageForSystemPackageWithKeysToExclude() {
-        mRestoreTask = new PerformUnifiedRestoreTask(Collections.singletonMap(
-                PACKAGE_NAME, mExcludedkeys));
+        when(mBackupManagerService.getExcludedRestoreKeys(eq(SYSTEM_PACKAGE_NAME))).thenReturn(
+                mExcludedkeys);
 
-        assertTrue(mRestoreTask.shouldStageBackupData(NON_SYSTEM_PACKAGE_NAME));
+        assertTrue(mRestoreTask.shouldStageBackupData(SYSTEM_PACKAGE_NAME));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 156cd6e..e5adb80 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -283,7 +283,7 @@
                 null /* authenticators */);
         waitForIdle();
         verify(mReceiver1).onError(
-                eq(BiometricAuthenticator.TYPE_NONE),
+                eq(BiometricAuthenticator.TYPE_FINGERPRINT),
                 eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE),
                 eq(0 /* vendorCode */));
     }
@@ -1117,14 +1117,14 @@
 
         // STRONG-only auth is not available
         int authenticators = Authenticators.BIOMETRIC_STRONG;
-        assertEquals(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
+        assertEquals(BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
                 invokeCanAuthenticate(mBiometricService, authenticators));
         invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
                 authenticators);
         waitForIdle();
         verify(mReceiver1).onError(
-                eq(BiometricAuthenticator.TYPE_NONE),
-                eq(BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT),
+                eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+                eq(BiometricPrompt.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED),
                 eq(0) /* vendorCode */);
 
         // Request for weak auth works
@@ -1154,7 +1154,7 @@
                 false /* requireConfirmation */,
                 authenticators);
         waitForIdle();
-        assertTrue(Utils.isDeviceCredentialAllowed(mBiometricService.mCurrentAuthSession.mBundle));
+        assertTrue(Utils.isCredentialRequested(mBiometricService.mCurrentAuthSession.mBundle));
         verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
                 eq(mBiometricService.mCurrentAuthSession.mBundle),
                 any(IBiometricServiceReceiverInternal.class),
@@ -1162,6 +1162,28 @@
                 anyBoolean() /* requireConfirmation */,
                 anyInt() /* userId */,
                 eq(TEST_PACKAGE_NAME));
+
+        // Un-downgrading the authenticator allows successful strong auth
+        for (BiometricService.AuthenticatorWrapper wrapper : mBiometricService.mAuthenticators) {
+            if (wrapper.id == testId) {
+                wrapper.updateStrength(Authenticators.BIOMETRIC_STRONG);
+            }
+        }
+
+        resetReceiver();
+        authenticators = Authenticators.BIOMETRIC_STRONG;
+        assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+                invokeCanAuthenticate(mBiometricService, authenticators));
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */, authenticators);
+        waitForIdle();
+        verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
+                eq(mBiometricService.mCurrentAuthSession.mBundle),
+                any(IBiometricServiceReceiverInternal.class),
+                eq(BiometricAuthenticator.TYPE_FINGERPRINT /* biometricModality */),
+                anyBoolean() /* requireConfirmation */,
+                anyInt() /* userId */,
+                eq(TEST_PACKAGE_NAME));
     }
 
     @Test(expected = IllegalStateException.class)
@@ -1245,6 +1267,19 @@
     }
 
     @Test
+    public void testAuthentication_normalAppIgnoresDevicePolicy() throws Exception {
+        setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+        when(mDevicePolicyManager
+                .getKeyguardDisabledFeatures(any() /* admin */, anyInt() /* userHandle */))
+                .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
+        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+                false /* requireConfirmation */, Authenticators.BIOMETRIC_STRONG);
+        waitForIdle();
+        assertEquals(mBiometricService.mCurrentAuthSession.mState,
+                BiometricService.STATE_AUTH_STARTED);
+    }
+
+    @Test
     public void testWorkAuthentication_faceWorksIfNotDisabledByDevicePolicyManager()
             throws Exception {
         setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
index 312ff2c..df242f3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
@@ -91,31 +91,31 @@
     @Test
     public void testIsDeviceCredentialAllowed_withIntegerFlags() {
         int authenticators = 0;
-        assertFalse(Utils.isDeviceCredentialAllowed(authenticators));
+        assertFalse(Utils.isCredentialRequested(authenticators));
 
         authenticators |= Authenticators.DEVICE_CREDENTIAL;
-        assertTrue(Utils.isDeviceCredentialAllowed(authenticators));
+        assertTrue(Utils.isCredentialRequested(authenticators));
 
         authenticators |= Authenticators.BIOMETRIC_WEAK;
-        assertTrue(Utils.isDeviceCredentialAllowed(authenticators));
+        assertTrue(Utils.isCredentialRequested(authenticators));
     }
 
     @Test
     public void testIsDeviceCredentialAllowed_withBundle() {
         Bundle bundle = new Bundle();
-        assertFalse(Utils.isDeviceCredentialAllowed(bundle));
+        assertFalse(Utils.isCredentialRequested(bundle));
 
         int authenticators = 0;
         bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
-        assertFalse(Utils.isDeviceCredentialAllowed(bundle));
+        assertFalse(Utils.isCredentialRequested(bundle));
 
         authenticators |= Authenticators.DEVICE_CREDENTIAL;
         bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
-        assertTrue(Utils.isDeviceCredentialAllowed(bundle));
+        assertTrue(Utils.isCredentialRequested(bundle));
 
         authenticators |= Authenticators.BIOMETRIC_WEAK;
         bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
-        assertTrue(Utils.isDeviceCredentialAllowed(bundle));
+        assertTrue(Utils.isCredentialRequested(bundle));
     }
 
     @Test
@@ -140,14 +140,14 @@
         for (int i = 0; i <= 7; i++) {
             int authenticators = 1 << i;
             bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
-            assertTrue(Utils.isBiometricAllowed(bundle));
+            assertTrue(Utils.isBiometricRequested(bundle));
         }
 
         // The rest of the bits are not allowed to integrate with the public APIs
         for (int i = 8; i < 32; i++) {
             int authenticators = 1 << i;
             bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
-            assertFalse(Utils.isBiometricAllowed(bundle));
+            assertFalse(Utils.isBiometricRequested(bundle));
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index ce5d6d9..4a686ee 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -19,6 +19,7 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
@@ -28,10 +29,12 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.compat.AndroidBuildClassifier;
+import com.android.server.LocalServices;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -48,6 +51,8 @@
     @Mock
     private PackageManager mPackageManager;
     @Mock
+    private PackageManagerInternal mPackageManagerInternal;
+    @Mock
     CompatChange.ChangeListener mListener1, mListener2;
     PlatformCompat mPlatformCompat;
     CompatConfig mCompatConfig;
@@ -60,11 +65,15 @@
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mPackageManager.getPackageUid(eq(PACKAGE_NAME), eq(0))).thenThrow(
                 new PackageManager.NameNotFoundException());
+        when(mPackageManagerInternal.getPackageUid(eq(PACKAGE_NAME), eq(0), anyInt()))
+            .thenReturn(-1);
         mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
         mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
         // Assume userdebug/eng non-final build
         when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
         when(mBuildClassifier.isFinalBuild()).thenReturn(false);
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
+        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index c9ec874..d40130a 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -281,9 +281,9 @@
         AppInstallMetadata appInstallMetadata = metadataCaptor.getValue();
         allowedInstallers = allowedInstallersCaptor.getValue();
         assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName());
-        assertEquals(APP_CERT, appInstallMetadata.getAppCertificate());
+        assertThat(appInstallMetadata.getAppCertificates()).containsExactly(APP_CERT);
         assertEquals(INSTALLER_SHA256, appInstallMetadata.getInstallerName());
-        assertEquals(INSTALLER_CERT, appInstallMetadata.getInstallerCertificate());
+        assertThat(appInstallMetadata.getInstallerCertificates()).containsExactly(INSTALLER_CERT);
         assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode());
         assertFalse(appInstallMetadata.isPreInstalled());
         // These are hardcoded in the test apk android manifest
diff --git a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
index 86daf69..41be54a 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/IntegrityFileManagerTest.java
@@ -141,10 +141,10 @@
         AppInstallMetadata appInstallMetadata =
                 new AppInstallMetadata.Builder()
                         .setPackageName(packageName)
-                        .setAppCertificate(packageCert)
+                        .setAppCertificates(Collections.singletonList(packageCert))
                         .setVersionCode(version)
                         .setInstallerName("abc")
-                        .setInstallerCertificate("abc")
+                        .setInstallerCertificates(Collections.singletonList("abc"))
                         .setIsPreInstalled(true)
                         .build();
         List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata);
@@ -182,10 +182,10 @@
         AppInstallMetadata appInstallMetadata =
                 new AppInstallMetadata.Builder()
                         .setPackageName(installedPackageName)
-                        .setAppCertificate(installedAppCertificate)
+                        .setAppCertificates(Collections.singletonList(installedAppCertificate))
                         .setVersionCode(250)
                         .setInstallerName("abc")
-                        .setInstallerCertificate("abc")
+                        .setInstallerCertificates(Collections.singletonList("abc"))
                         .setIsPreInstalled(true)
                         .build();
         List<Rule> rulesFetched = mIntegrityFileManager.readRules(appInstallMetadata);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
index 26b2096..b0b9596 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluationEngineTest.java
@@ -70,17 +70,17 @@
         AppInstallMetadata appInstallMetadata1 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_1)
-                        .setInstallerCertificate(INSTALLER_1_CERT)
+                        .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
                         .build();
         AppInstallMetadata appInstallMetadata2 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_2)
-                        .setInstallerCertificate(INSTALLER_2_CERT)
+                        .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT))
                         .build();
         AppInstallMetadata appInstallMetadata3 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(RANDOM_INSTALLER)
-                        .setInstallerCertificate(RANDOM_INSTALLER_CERT)
+                        .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT))
                         .build();
 
         assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect())
@@ -99,7 +99,7 @@
         AppInstallMetadata appInstallMetadata1 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_1)
-                        .setInstallerCertificate(INSTALLER_1_CERT)
+                        .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
                         .build();
         assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
@@ -107,7 +107,7 @@
         AppInstallMetadata appInstallMetadata2 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(RANDOM_INSTALLER)
-                        .setInstallerCertificate(INSTALLER_1_CERT)
+                        .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
                         .build();
         assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.DENY);
@@ -115,7 +115,7 @@
         AppInstallMetadata appInstallMetadata3 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_1)
-                        .setInstallerCertificate(RANDOM_INSTALLER_CERT)
+                        .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT))
                         .build();
         assertThat(mEngine.evaluate(appInstallMetadata3, allowedInstallers).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.DENY);
@@ -123,7 +123,7 @@
         AppInstallMetadata appInstallMetadata4 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_1)
-                        .setInstallerCertificate(RANDOM_INSTALLER_CERT)
+                        .setInstallerCertificates(Collections.singletonList(RANDOM_INSTALLER_CERT))
                         .build();
         assertThat(mEngine.evaluate(appInstallMetadata4, allowedInstallers).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.DENY);
@@ -138,7 +138,7 @@
         AppInstallMetadata appInstallMetadata1 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_1)
-                        .setInstallerCertificate(INSTALLER_1_CERT)
+                        .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
                         .build();
         assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
@@ -146,7 +146,7 @@
         AppInstallMetadata appInstallMetadata2 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_2)
-                        .setInstallerCertificate(INSTALLER_2_CERT)
+                        .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT))
                         .build();
         assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
@@ -154,7 +154,7 @@
         AppInstallMetadata appInstallMetadata3 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_1)
-                        .setInstallerCertificate(INSTALLER_2_CERT)
+                        .setInstallerCertificates(Collections.singletonList(INSTALLER_2_CERT))
                         .build();
         assertThat(mEngine.evaluate(appInstallMetadata3, allowedInstallers).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.DENY);
@@ -162,7 +162,7 @@
         AppInstallMetadata appInstallMetadata4 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_2)
-                        .setInstallerCertificate(INSTALLER_1_CERT)
+                        .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
                         .build();
         assertThat(mEngine.evaluate(appInstallMetadata4, allowedInstallers).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.DENY);
@@ -178,7 +178,7 @@
         AppInstallMetadata appInstallMetadata1 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(INSTALLER_1)
-                        .setInstallerCertificate(INSTALLER_1_CERT)
+                        .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
                         .build();
         assertThat(mEngine.evaluate(appInstallMetadata1, allowedInstallers).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.ALLOW);
@@ -186,7 +186,7 @@
         AppInstallMetadata appInstallMetadata2 =
                 getAppInstallMetadataBuilder()
                         .setInstallerName(RANDOM_INSTALLER)
-                        .setInstallerCertificate(INSTALLER_1_CERT)
+                        .setInstallerCertificates(Collections.singletonList(INSTALLER_1_CERT))
                         .build();
         assertThat(mEngine.evaluate(appInstallMetadata2, allowedInstallers).getEffect())
                 .isEqualTo(IntegrityCheckResult.Effect.DENY);
@@ -196,8 +196,8 @@
     private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
         return new AppInstallMetadata.Builder()
                 .setPackageName("abc")
-                .setAppCertificate("abc")
-                .setInstallerCertificate("abc")
+                .setAppCertificates(Collections.singletonList("abc"))
+                .setInstallerCertificates(Collections.singletonList("abc"))
                 .setInstallerName("abc")
                 .setVersionCode(-1)
                 .setIsPreInstalled(true);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
index 7b53e5e..b271a77 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/engine/RuleEvaluatorTest.java
@@ -48,7 +48,7 @@
     private static final AppInstallMetadata APP_INSTALL_METADATA =
             new AppInstallMetadata.Builder()
                     .setPackageName(PACKAGE_NAME_1)
-                    .setAppCertificate(APP_CERTIFICATE)
+                    .setAppCertificates(Collections.singletonList(APP_CERTIFICATE))
                     .setVersionCode(2)
                     .build();
 
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
index 742952e..0784b7a 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleIndexingControllerTest.java
@@ -35,6 +35,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 @RunWith(JUnit4.class)
@@ -49,7 +51,7 @@
         AppInstallMetadata appInstallMetadata =
                 new AppInstallMetadata.Builder()
                         .setPackageName("ddd")
-                        .setAppCertificate("777")
+                        .setAppCertificates(Collections.singletonList("777"))
                         .build();
 
         List<RuleIndexRange> resultingIndexes =
@@ -63,6 +65,29 @@
     }
 
     @Test
+    public void verifyIndexRangeSearchIsCorrect_multipleAppCertificates() throws IOException {
+        InputStream inputStream = obtainDefaultIndexingMapForTest();
+
+        RuleIndexingController indexingController = new RuleIndexingController(inputStream);
+
+        AppInstallMetadata appInstallMetadata =
+                new AppInstallMetadata.Builder()
+                        .setPackageName("ddd")
+                        .setAppCertificates(Arrays.asList("777", "999"))
+                        .build();
+
+        List<RuleIndexRange> resultingIndexes =
+                indexingController.identifyRulesToEvaluate(appInstallMetadata);
+
+        assertThat(resultingIndexes)
+                .containsExactly(
+                        new RuleIndexRange(200, 300),
+                        new RuleIndexRange(700, 800),
+                        new RuleIndexRange(800, 900),
+                        new RuleIndexRange(900, 945));
+    }
+
+    @Test
     public void verifyIndexRangeSearchIsCorrect_keysInFirstAndLastBlock() throws IOException {
         InputStream inputStream = obtainDefaultIndexingMapForTest();
 
@@ -71,7 +96,7 @@
         AppInstallMetadata appInstallMetadata =
                 new AppInstallMetadata.Builder()
                         .setPackageName("bbb")
-                        .setAppCertificate("999")
+                        .setAppCertificates(Collections.singletonList("999"))
                         .build();
 
         List<RuleIndexRange> resultingIndexes =
@@ -93,7 +118,7 @@
         AppInstallMetadata appInstallMetadata =
                 new AppInstallMetadata.Builder()
                         .setPackageName("ccc")
-                        .setAppCertificate("444")
+                        .setAppCertificates(Collections.singletonList("444"))
                         .build();
 
         List<RuleIndexRange> resultingIndexes =
@@ -125,7 +150,7 @@
         AppInstallMetadata appInstallMetadata =
                 new AppInstallMetadata.Builder()
                         .setPackageName("ccc")
-                        .setAppCertificate("444")
+                        .setAppCertificates(Collections.singletonList("444"))
                         .build();
 
         List<RuleIndexRange> resultingIndexes =
diff --git a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
index f16cf35..4ba584e 100644
--- a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
@@ -39,6 +39,7 @@
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementsEvent;
 import android.location.GnssNavigationMessage;
+import android.location.GnssRequest;
 import android.location.GnssSingleSatCorrection;
 import android.location.IBatchedLocationCallback;
 import android.location.IGnssMeasurementsListener;
@@ -563,7 +564,7 @@
 
         assertThrows(SecurityException.class,
                 () -> mGnssManagerService.addGnssMeasurementsListener(
-                        mockGnssMeasurementsListener,
+                        new GnssRequest.Builder().build(), mockGnssMeasurementsListener,
                         "com.android.server", "abcd123", "TestGnssMeasurementsListener"));
 
         mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
@@ -580,8 +581,11 @@
 
         enableLocationPermissions();
 
-        assertThat(mGnssManagerService.addGnssMeasurementsListener(mockGnssMeasurementsListener,
-                "com.android.server", "abcd123", "TestGnssMeasurementsListener")).isEqualTo(true);
+        assertThat(mGnssManagerService.addGnssMeasurementsListener(
+                new GnssRequest.Builder().build(),
+                mockGnssMeasurementsListener,
+                "com.android.server", "abcd123",
+                "TestGnssMeasurementsListener")).isEqualTo(true);
 
         mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
         verify(mockGnssMeasurementsListener, times(1)).onGnssMeasurementsReceived(
@@ -626,8 +630,10 @@
 
         enableLocationPermissions();
 
-        mGnssManagerService.addGnssMeasurementsListener(mockGnssMeasurementsListener,
-                "com.android.server", "abcd123", "TestGnssMeasurementsListener");
+        mGnssManagerService.addGnssMeasurementsListener(new GnssRequest.Builder().build(),
+                mockGnssMeasurementsListener,
+                "com.android.server", "abcd123",
+                "TestGnssMeasurementsListener");
 
         disableLocationPermissions();
 
@@ -648,8 +654,10 @@
 
         enableLocationPermissions();
 
-        mGnssManagerService.addGnssMeasurementsListener(mockGnssMeasurementsListener,
-                "com.android.server", "abcd123", "TestGnssMeasurementsListener");
+        mGnssManagerService.addGnssMeasurementsListener(new GnssRequest.Builder().build(),
+                mockGnssMeasurementsListener,
+                "com.android.server", "abcd123",
+                "TestGnssMeasurementsListener");
 
         disableLocationPermissions();
 
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
index 331ad59..03b5e38 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationStoreTest.java
@@ -21,16 +21,24 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import android.annotation.Nullable;
+import android.content.Context;
 import android.content.LocusId;
 import android.content.pm.ShortcutInfo;
 import android.net.Uri;
+import android.os.FileUtils;
+import android.text.format.DateUtils;
 import android.util.ArraySet;
 
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.io.File;
 import java.util.Set;
 
 @RunWith(JUnit4.class)
@@ -42,11 +50,34 @@
     private static final Uri CONTACT_URI = Uri.parse("tel:+1234567890");
     private static final String PHONE_NUMBER = "+1234567890";
 
+    private static final String SHORTCUT_ID_2 = "ghi";
+    private static final String NOTIFICATION_CHANNEL_ID_2 = "test : ghi";
+    private static final LocusId LOCUS_ID_2 = new LocusId("jkl");
+    private static final Uri CONTACT_URI_2 = Uri.parse("tel:+3234567890");
+    private static final String PHONE_NUMBER_2 = "+3234567890";
+
+    private static final String SHORTCUT_ID_3 = "mno";
+    private static final String NOTIFICATION_CHANNEL_ID_3 = "test : mno";
+    private static final LocusId LOCUS_ID_3 = new LocusId("pqr");
+    private static final Uri CONTACT_URI_3 = Uri.parse("tel:+9234567890");
+    private static final String PHONE_NUMBER_3 = "+9234567890";
+
+    private MockScheduledExecutorService mMockScheduledExecutorService;
+    private TestContactQueryHelper mTestContactQueryHelper;
     private ConversationStore mConversationStore;
+    private File mFile;
 
     @Before
     public void setUp() {
-        mConversationStore = new ConversationStore();
+        Context ctx = InstrumentationRegistry.getContext();
+        mFile = new File(ctx.getCacheDir(), "testdir");
+        mTestContactQueryHelper = new TestContactQueryHelper(ctx);
+        resetConversationStore();
+    }
+
+    @After
+    public void tearDown() {
+        FileUtils.deleteContentsAndDir(mFile);
     }
 
     @Test
@@ -153,6 +184,130 @@
                 mConversationStore.getConversationByNotificationChannelId(NOTIFICATION_CHANNEL_ID));
     }
 
+    @Test
+    public void testDataPersistenceAndRestoration() {
+        // Add conversation infos, causing it to be loaded to disk.
+        ConversationInfo in1 = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+                PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
+        ConversationInfo in2 = buildConversationInfo(SHORTCUT_ID_2, LOCUS_ID_2, CONTACT_URI_2,
+                PHONE_NUMBER_2, NOTIFICATION_CHANNEL_ID_2);
+        ConversationInfo in3 = buildConversationInfo(SHORTCUT_ID_3, LOCUS_ID_3, CONTACT_URI_3,
+                PHONE_NUMBER_3, NOTIFICATION_CHANNEL_ID_3);
+        mConversationStore.addOrUpdate(in1);
+        mConversationStore.addOrUpdate(in2);
+        mConversationStore.addOrUpdate(in3);
+
+        long futuresExecuted = mMockScheduledExecutorService.fastForwardTime(
+                3L * DateUtils.MINUTE_IN_MILLIS);
+        assertEquals(1, futuresExecuted);
+
+        mMockScheduledExecutorService.resetTimeElapsedMillis();
+
+        // During restoration, we want to confirm that this conversation was removed.
+        mConversationStore.deleteConversation(SHORTCUT_ID_3);
+        mMockScheduledExecutorService.fastForwardTime(3L * DateUtils.MINUTE_IN_MILLIS);
+
+        mTestContactQueryHelper.setQueryResult(true, true);
+        mTestContactQueryHelper.setPhoneNumberResult(PHONE_NUMBER, PHONE_NUMBER_2);
+
+        resetConversationStore();
+        ConversationInfo out1 = mConversationStore.getConversation(SHORTCUT_ID);
+        ConversationInfo out2 = mConversationStore.getConversation(SHORTCUT_ID_2);
+        ConversationInfo out3 = mConversationStore.getConversation(SHORTCUT_ID_3);
+        mConversationStore.deleteConversation(SHORTCUT_ID);
+        mConversationStore.deleteConversation(SHORTCUT_ID_2);
+        mConversationStore.deleteConversation(SHORTCUT_ID_3);
+        assertEquals(in1, out1);
+        assertEquals(in2, out2);
+        assertNull(out3);
+    }
+
+    @Test
+    public void testDelayedDiskWrites() {
+        ConversationInfo in1 = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+                PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
+        ConversationInfo in2 = buildConversationInfo(SHORTCUT_ID_2, LOCUS_ID_2, CONTACT_URI_2,
+                PHONE_NUMBER_2, NOTIFICATION_CHANNEL_ID_2);
+        ConversationInfo in3 = buildConversationInfo(SHORTCUT_ID_3, LOCUS_ID_3, CONTACT_URI_3,
+                PHONE_NUMBER_3, NOTIFICATION_CHANNEL_ID_3);
+
+        mConversationStore.addOrUpdate(in1);
+        mMockScheduledExecutorService.fastForwardTime(3L * DateUtils.MINUTE_IN_MILLIS);
+        mMockScheduledExecutorService.resetTimeElapsedMillis();
+
+        // Should not see second conversation on disk because of disk write delay has not been
+        // reached.
+        mConversationStore.addOrUpdate(in2);
+        mMockScheduledExecutorService.fastForwardTime(DateUtils.MINUTE_IN_MILLIS);
+
+        mTestContactQueryHelper.setQueryResult(true);
+        mTestContactQueryHelper.setPhoneNumberResult(PHONE_NUMBER);
+
+        resetConversationStore();
+        ConversationInfo out1 = mConversationStore.getConversation(SHORTCUT_ID);
+        ConversationInfo out2 = mConversationStore.getConversation(SHORTCUT_ID_2);
+        assertEquals(in1, out1);
+        assertNull(out2);
+
+        mConversationStore.addOrUpdate(in2);
+        mMockScheduledExecutorService.fastForwardTime(3L * DateUtils.MINUTE_IN_MILLIS);
+        mMockScheduledExecutorService.resetTimeElapsedMillis();
+
+        mConversationStore.addOrUpdate(in3);
+        mMockScheduledExecutorService.fastForwardTime(3L * DateUtils.MINUTE_IN_MILLIS);
+
+        mTestContactQueryHelper.reset();
+        mTestContactQueryHelper.setQueryResult(true, true, true);
+        mTestContactQueryHelper.setPhoneNumberResult(PHONE_NUMBER, PHONE_NUMBER_2, PHONE_NUMBER_3);
+
+        resetConversationStore();
+        out1 = mConversationStore.getConversation(SHORTCUT_ID);
+        out2 = mConversationStore.getConversation(SHORTCUT_ID_2);
+        ConversationInfo out3 = mConversationStore.getConversation(SHORTCUT_ID_3);
+        assertEquals(in1, out1);
+        assertEquals(in2, out2);
+        assertEquals(in3, out3);
+    }
+
+    @Test
+    public void testMimicDevicePowerOff() {
+
+        // Even without fast forwarding time with our mock ScheduledExecutorService, we should
+        // see the conversations immediately saved to disk.
+        ConversationInfo in1 = buildConversationInfo(SHORTCUT_ID, LOCUS_ID, CONTACT_URI,
+                PHONE_NUMBER, NOTIFICATION_CHANNEL_ID);
+        ConversationInfo in2 = buildConversationInfo(SHORTCUT_ID_2, LOCUS_ID_2, CONTACT_URI_2,
+                PHONE_NUMBER_2, NOTIFICATION_CHANNEL_ID_2);
+
+        mConversationStore.addOrUpdate(in1);
+        mConversationStore.addOrUpdate(in2);
+        mConversationStore.saveConversationsToDisk();
+
+        // Ensure that futures were cancelled and the immediate flush occurred.
+        assertEquals(0, mMockScheduledExecutorService.getFutures().size());
+
+        // Expect to see 2 executes: loadConversationFromDisk and saveConversationsToDisk.
+        // loadConversationFromDisk gets called each time we call #resetConversationStore().
+        assertEquals(2, mMockScheduledExecutorService.getExecutes().size());
+
+        mTestContactQueryHelper.setQueryResult(true, true);
+        mTestContactQueryHelper.setPhoneNumberResult(PHONE_NUMBER, PHONE_NUMBER_2);
+
+        resetConversationStore();
+        ConversationInfo out1 = mConversationStore.getConversation(SHORTCUT_ID);
+        ConversationInfo out2 = mConversationStore.getConversation(SHORTCUT_ID_2);
+        assertEquals(in1, out1);
+        assertEquals(in2, out2);
+    }
+
+    private void resetConversationStore() {
+        mFile.mkdir();
+        mMockScheduledExecutorService = new MockScheduledExecutorService();
+        mConversationStore = new ConversationStore(mFile, mMockScheduledExecutorService,
+                mTestContactQueryHelper);
+        mConversationStore.loadConversationsFromDisk();
+    }
+
     private static ConversationInfo buildConversationInfo(String shortcutId) {
         return buildConversationInfo(shortcutId, null, null, null, null);
     }
@@ -171,4 +326,54 @@
                 .setBubbled(true)
                 .build();
     }
+
+    private static class TestContactQueryHelper extends ContactsQueryHelper {
+
+        private int mQueryCalls;
+        private boolean[] mQueryResult;
+
+        private int mPhoneNumberCalls;
+        private String[] mPhoneNumberResult;
+
+        TestContactQueryHelper(Context context) {
+            super(context);
+
+            mQueryCalls = 0;
+            mPhoneNumberCalls = 0;
+        }
+
+        private void setQueryResult(boolean... values) {
+            mQueryResult = values;
+        }
+
+        private void setPhoneNumberResult(String... values) {
+            mPhoneNumberResult = values;
+        }
+
+        private void reset() {
+            mQueryCalls = 0;
+            mQueryResult = null;
+            mPhoneNumberCalls = 0;
+            mPhoneNumberResult = null;
+        }
+
+        @Override
+        boolean query(String contactUri) {
+            if (mQueryResult != null && mQueryCalls < mQueryResult.length) {
+                return mQueryResult[mQueryCalls++];
+            }
+            mQueryCalls++;
+            return false;
+        }
+
+        @Override
+        @Nullable
+        String getPhoneNumber() {
+            if (mPhoneNumberResult != null && mPhoneNumberCalls < mPhoneNumberResult.length) {
+                return mPhoneNumberResult[mPhoneNumberCalls++];
+            }
+            mPhoneNumberCalls++;
+            return null;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java b/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java
new file mode 100644
index 0000000..8b8ba12
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/people/data/MockScheduledExecutorService.java
@@ -0,0 +1,238 @@
+/*
+ * 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.people.data;
+
+import com.android.internal.util.Preconditions;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Delayed;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Mock implementation of ScheduledExecutorService for testing. All commands will run
+ * synchronously. Commands passed to {@link #submit(Runnable)} and {@link #execute(Runnable)} will
+ * run immediately. Commands scheduled via {@link #schedule(Runnable, long, TimeUnit)} will run
+ * after calling {@link #fastForwardTime(long)}.
+ */
+class MockScheduledExecutorService implements ScheduledExecutorService {
+
+    private final List<Runnable> mExecutes = new ArrayList<>();
+    private final List<MockScheduledFuture<?>> mFutures = new ArrayList<>();
+    private long mTimeElapsedMillis = 0;
+
+    /**
+     * Advances fake time, runs all the commands for which the delay has expired.
+     */
+    long fastForwardTime(long millis) {
+        mTimeElapsedMillis += millis;
+        ImmutableList<MockScheduledFuture<?>> futuresCopy = ImmutableList.copyOf(mFutures);
+        mFutures.clear();
+        long totalExecuted = 0;
+        for (MockScheduledFuture<?> future : futuresCopy) {
+            if (future.getDelay() < mTimeElapsedMillis) {
+                future.getCommand().run();
+                mExecutes.add(future.getCommand());
+                totalExecuted += 1;
+            } else {
+                mFutures.add(future);
+            }
+        }
+        return totalExecuted;
+    }
+
+    List<Runnable> getExecutes() {
+        return mExecutes;
+    }
+
+    List<MockScheduledFuture<?>> getFutures() {
+        return mFutures;
+    }
+
+    void resetTimeElapsedMillis() {
+        mTimeElapsedMillis = 0;
+    }
+
+    /**
+     * Fakes a schedule execution of {@link Runnable}. The command will be executed by an explicit
+     * call to {@link #fastForwardTime(long)}.
+     */
+    @Override
+    public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
+        Preconditions.checkState(unit == TimeUnit.MILLISECONDS);
+        MockScheduledFuture<?> future = new MockScheduledFuture<>(command, delay, unit);
+        mFutures.add(future);
+        return future;
+    }
+
+    @Override
+    public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period,
+            TimeUnit unit) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay,
+            long delay, TimeUnit unit) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void shutdown() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public List<Runnable> shutdownNow() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isShutdown() {
+        return false;
+    }
+
+    @Override
+    public boolean isTerminated() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public <T> Future<T> submit(Callable<T> task) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public <T> Future<T> submit(Runnable task, T result) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Future<?> submit(Runnable command) {
+        mExecutes.add(command);
+        MockScheduledFuture<?> future = new MockScheduledFuture<>(command, 0,
+                TimeUnit.MILLISECONDS);
+        future.getCommand().run();
+        return future;
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
+            throws InterruptedException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout,
+            TimeUnit unit) throws InterruptedException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
+            throws ExecutionException, InterruptedException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
+            throws ExecutionException, InterruptedException, TimeoutException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        mExecutes.add(command);
+        command.run();
+    }
+
+    class MockScheduledFuture<V> implements ScheduledFuture<V> {
+
+        private final Runnable mCommand;
+        private final long mDelay;
+        private boolean mCancelled = false;
+
+        MockScheduledFuture(Runnable command, long delay, TimeUnit timeUnit) {
+            mCommand = command;
+            mDelay = delay;
+        }
+
+        public long getDelay() {
+            return mDelay;
+        }
+
+        public Runnable getCommand() {
+            return mCommand;
+        }
+
+        @Override
+        public long getDelay(TimeUnit unit) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int compareTo(Delayed o) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            mCancelled = true;
+            return mFutures.remove(this);
+        }
+
+        @Override
+        public boolean isCancelled() {
+            return mCancelled;
+        }
+
+        @Override
+        public boolean isDone() {
+            return !mFutures.contains(this);
+        }
+
+        @Override
+        public V get() throws ExecutionException, InterruptedException {
+            return null;
+        }
+
+        @Override
+        public V get(long timeout, TimeUnit unit)
+                throws ExecutionException, InterruptedException, TimeoutException {
+            return null;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
index ec4789a..1ddc21e 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
@@ -20,15 +20,19 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
+import android.content.Context;
 import android.content.LocusId;
 import android.content.pm.ShortcutInfo;
 import android.net.Uri;
 
+import androidx.test.InstrumentationRegistry;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.io.File;
 import java.util.List;
 
 @RunWith(JUnit4.class)
@@ -52,8 +56,12 @@
 
     @Before
     public void setUp() {
+        Context ctx = InstrumentationRegistry.getContext();
+        File testDir = new File(ctx.getCacheDir(), "testdir");
+        testDir.mkdir();
         mPackageData = new PackageData(
-                PACKAGE_NAME, USER_ID, pkg -> mIsDefaultDialer, pkg -> mIsDefaultSmsApp);
+                PACKAGE_NAME, USER_ID, pkg -> mIsDefaultDialer, pkg -> mIsDefaultSmsApp,
+                new MockScheduledExecutorService(), testDir, new ContactsQueryHelper(ctx));
         ConversationInfo conversationInfo = new ConversationInfo.Builder()
                 .setShortcutId(SHORTCUT_ID)
                 .setLocusId(LOCUS_ID)
diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
index e4248a0..b273578 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
@@ -29,8 +29,11 @@
 import android.annotation.UserIdInt;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
+import android.content.Context;
 import android.content.LocusId;
 
+import androidx.test.InstrumentationRegistry;
+
 import com.android.server.LocalServices;
 
 import org.junit.After;
@@ -41,10 +44,12 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
 import java.util.function.Predicate;
 
 @RunWith(JUnit4.class)
@@ -69,7 +74,13 @@
 
         addLocalServiceMock(UsageStatsManagerInternal.class, mUsageStatsManagerInternal);
 
-        mPackageData = new TestPackageData(PKG_NAME, USER_ID_PRIMARY, pkg -> false, pkg -> false);
+        Context ctx = InstrumentationRegistry.getContext();
+        File testDir = new File(ctx.getCacheDir(), "testdir");
+        ScheduledExecutorService scheduledExecutorService = new MockScheduledExecutorService();
+        ContactsQueryHelper helper = new ContactsQueryHelper(ctx);
+
+        mPackageData = new TestPackageData(PKG_NAME, USER_ID_PRIMARY, pkg -> false, pkg -> false,
+                scheduledExecutorService, testDir, helper);
         mPackageData.mConversationStore.mConversationInfo = new ConversationInfo.Builder()
                 .setShortcutId(SHORTCUT_ID)
                 .setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
@@ -175,7 +186,7 @@
         assertEquals(createInAppConversationEvent(130_000L, 30), events.get(2));
     }
 
-    private void addUsageEvents(UsageEvents.Event ... events) {
+    private void addUsageEvents(UsageEvents.Event... events) {
         UsageEvents usageEvents = new UsageEvents(Arrays.asList(events), new String[]{});
         when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(),
                 anyBoolean(), anyBoolean())).thenReturn(usageEvents);
@@ -228,6 +239,12 @@
 
         private ConversationInfo mConversationInfo;
 
+        TestConversationStore(File packageDir,
+                ScheduledExecutorService scheduledExecutorService,
+                ContactsQueryHelper helper) {
+            super(packageDir, scheduledExecutorService, helper);
+        }
+
         @Override
         @Nullable
         ConversationInfo getConversation(@Nullable String shortcutId) {
@@ -237,13 +254,18 @@
 
     private static class TestPackageData extends PackageData {
 
-        private final TestConversationStore mConversationStore = new TestConversationStore();
+        private final TestConversationStore mConversationStore;
         private final TestEventStore mEventStore = new TestEventStore();
 
         TestPackageData(@NonNull String packageName, @UserIdInt int userId,
                 @NonNull Predicate<String> isDefaultDialerPredicate,
-                @NonNull Predicate<String> isDefaultSmsAppPredicate) {
-            super(packageName, userId, isDefaultDialerPredicate, isDefaultSmsAppPredicate);
+                @NonNull Predicate<String> isDefaultSmsAppPredicate,
+                @NonNull ScheduledExecutorService scheduledExecutorService, @NonNull File rootDir,
+                @NonNull ContactsQueryHelper helper) {
+            super(packageName, userId, isDefaultDialerPredicate, isDefaultSmsAppPredicate,
+                    scheduledExecutorService, rootDir, helper);
+            mConversationStore = new TestConversationStore(rootDir, scheduledExecutorService,
+                    helper);
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 3e3f40d..e28d13a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -35,7 +35,6 @@
 import android.content.pm.parsing.ComponentParseUtils.ParsedActivityIntentInfo;
 import android.content.pm.parsing.PackageImpl;
 import android.content.pm.parsing.ParsingPackage;
-import android.net.Uri;
 import android.os.Build;
 import android.os.Process;
 import android.util.ArrayMap;
@@ -87,6 +86,17 @@
         return pkg;
     }
 
+    private static ParsingPackage pkgQueriesProvider(String packageName,
+            String... queriesAuthorities) {
+        ParsingPackage pkg = pkg(packageName);
+        if (queriesAuthorities != null) {
+            for (String authority : queriesAuthorities) {
+                pkg.addQueriesProvider(authority);
+            }
+        }
+        return pkg;
+    }
+
     private static ParsingPackage pkg(String packageName, String... queriesPackages) {
         ParsingPackage pkg = pkg(packageName);
         if (queriesPackages != null) {
@@ -172,8 +182,7 @@
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_UID);
         PackageSetting calling = simulateAddPackage(appsFilter,
-                pkg("com.some.other.package",
-                        new Intent().setData(Uri.parse("content://com.some.authority"))),
+                pkgQueriesProvider("com.some.other.package", "com.some.authority"),
                 DUMMY_CALLING_UID);
 
         assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
@@ -188,8 +197,7 @@
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_UID);
         PackageSetting calling = simulateAddPackage(appsFilter,
-                pkg("com.some.other.package",
-                        new Intent().setData(Uri.parse("content://com.some.other.authority"))),
+                pkgQueriesProvider("com.some.other.package", "com.some.other.authority"),
                 DUMMY_CALLING_UID);
 
         assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
@@ -205,8 +213,7 @@
                 pkgWithProvider("com.some.package", "com.some.authority;com.some.other.authority"),
                 DUMMY_TARGET_UID);
         PackageSetting calling = simulateAddPackage(appsFilter,
-                pkg("com.some.other.package",
-                        new Intent().setData(Uri.parse("content://com.some.authority"))),
+                pkgQueriesProvider("com.some.other.package", "com.some.authority"),
                 DUMMY_CALLING_UID);
 
         assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
@@ -266,7 +273,7 @@
         appsFilter.onSystemReady();
 
         PackageSetting target = simulateAddPackage(appsFilter,
-                        pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_UID);
+                pkg("com.some.package").setForceQueryable(true), DUMMY_TARGET_UID);
         PackageSetting calling = simulateAddPackage(appsFilter,
                 pkg("com.some.other.package"), DUMMY_CALLING_UID);
 
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 df2b3ef..77f842a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -24,7 +24,6 @@
 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;
@@ -1729,7 +1728,6 @@
         final ArgumentCaptor<Intent[]> intentsCaptor = ArgumentCaptor.forClass(Intent[].class);
         verify(mMockActivityTaskManagerInternal).startActivitiesAsPackage(
                 eq(packageName),
-                isNull(),
                 eq(userId),
                 intentsCaptor.capture(),
                 anyOrNull(Bundle.class));
@@ -1788,7 +1786,6 @@
         // 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 fea1b82..91cc9f3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/CrossProfileAppsServiceImplTest.java
@@ -55,7 +55,6 @@
 @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");
@@ -221,7 +220,6 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
-                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PRIMARY_USER).getIdentifier(),
                                 true));
@@ -230,7 +228,6 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
-                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -244,7 +241,6 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
-                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PRIMARY_USER).getIdentifier(),
                                 false));
@@ -253,7 +249,6 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
-                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -269,7 +264,6 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
-                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 true));
@@ -278,7 +272,6 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
-                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -294,7 +287,6 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
-                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 false));
@@ -303,7 +295,6 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
-                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -317,7 +308,6 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_TWO,
-                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 true));
@@ -326,7 +316,6 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
-                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -340,7 +329,6 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_TWO,
-                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 false));
@@ -349,7 +337,6 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
-                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -365,7 +352,6 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
-                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 true));
@@ -374,7 +360,6 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
-                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -395,7 +380,6 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
-                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 false));
@@ -404,7 +388,6 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
-                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -418,7 +401,6 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
-                                FEATURE_ID,
                                 new ComponentName(PACKAGE_TWO, "test"),
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 true));
@@ -427,7 +409,6 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
-                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -441,7 +422,6 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
-                                FEATURE_ID,
                                 new ComponentName(PACKAGE_TWO, "test"),
                                 UserHandle.of(PROFILE_OF_PRIMARY_USER).getIdentifier(),
                                 false));
@@ -450,7 +430,6 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
-                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -464,7 +443,6 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
-                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(SECONDARY_USER).getIdentifier(),
                                 true));
@@ -473,7 +451,6 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
-                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -487,7 +464,6 @@
                         mCrossProfileAppsServiceImpl.startActivityAsUser(
                                 mIApplicationThread,
                                 PACKAGE_ONE,
-                                FEATURE_ID,
                                 ACTIVITY_COMPONENT,
                                 UserHandle.of(SECONDARY_USER).getIdentifier(),
                                 false));
@@ -496,7 +472,6 @@
                 .startActivityAsUser(
                         nullable(IApplicationThread.class),
                         anyString(),
-                        nullable(String.class),
                         any(Intent.class),
                         nullable(Bundle.class),
                         anyInt());
@@ -509,7 +484,6 @@
         mCrossProfileAppsServiceImpl.startActivityAsUser(
                 mIApplicationThread,
                 PACKAGE_ONE,
-                FEATURE_ID,
                 ACTIVITY_COMPONENT,
                 UserHandle.of(PRIMARY_USER).getIdentifier(),
                 true);
@@ -518,7 +492,6 @@
                 .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 2936bdd..f036708 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(), anyStringOrNull(), anyInt(),
+                    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(), anyStringOrNull(), anyInt(),
+                            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/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index 804c1b9..1a6c6b4 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -224,21 +224,43 @@
     }
 
     @Test
-    public void snapshotThenDelete() {
+    public void snapshotThenDeleteNoApex() {
         Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
         PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
-        PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+        PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, false);
         rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
 
-        int[] userIds = {12, 18};
+        int[] userIds = {111, 222};
         rollback.snapshotUserData(PKG_2, userIds, mMockDataHelper);
 
         verify(mMockDataHelper).snapshotAppData(eq(123), pkgRollbackInfoFor(PKG_2), eq(userIds));
 
         rollback.delete(mMockDataHelper);
 
-        verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(12));
-        verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(18));
+        verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(111));
+        verify(mMockDataHelper).destroyAppDataSnapshot(eq(123), pkgRollbackInfoFor(PKG_2), eq(222));
+        verify(mMockDataHelper, never()).destroyApexDeSnapshots(anyInt());
+
+        assertThat(rollback.isDeleted()).isTrue();
+    }
+
+    @Test
+    public void snapshotThenDeleteWithApex() {
+        Rollback rollback = new Rollback(123, new File("/test/testing"), -1, USER, INSTALLER);
+        PackageRollbackInfo pkgInfo1 = newPkgInfoFor(PKG_1, 12, 10, false);
+        PackageRollbackInfo pkgInfo2 = newPkgInfoFor(PKG_2, 18, 12, true);
+        rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2));
+
+        int[] userIds = {111, 222};
+        rollback.snapshotUserData(PKG_2, userIds, mMockDataHelper);
+
+        verify(mMockDataHelper).snapshotAppData(eq(123), pkgRollbackInfoFor(PKG_2), eq(userIds));
+
+        rollback.delete(mMockDataHelper);
+
+        verify(mMockDataHelper, never())
+                .destroyAppDataSnapshot(anyInt(), pkgRollbackInfoFor(PKG_2), anyInt());
+        verify(mMockDataHelper).destroyApexDeSnapshots(123);
 
         assertThat(rollback.isDeleted()).isTrue();
     }
diff --git a/services/tests/uiservicestests/Android.bp b/services/tests/uiservicestests/Android.bp
index f608bab..3f0cda3 100644
--- a/services/tests/uiservicestests/Android.bp
+++ b/services/tests/uiservicestests/Android.bp
@@ -47,7 +47,6 @@
         "libbacktrace",
         "libbase",
         "libbinder",
-        "libbinderthreadstate",
         "libc++",
         "libcutils",
         "liblog",
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 9ec339d..57428dc 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -32,7 +32,6 @@
 import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED;
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
-import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
@@ -52,6 +51,8 @@
 import static android.os.UserHandle.USER_SYSTEM;
 import static android.service.notification.Adjustment.KEY_IMPORTANCE;
 import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
+import static android.service.notification.NotificationListenerService.REASON_APP_CANCEL;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
 
@@ -1147,7 +1148,9 @@
         waitForIdle();
         assertEquals(1, mNotificationRecordLogger.getCalls().size());
         NotificationRecordLoggerFake.CallRecord call = mNotificationRecordLogger.get(0);
-        assertTrue(call.shouldLog());
+        assertTrue(call.shouldLog);
+        assertEquals(NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
+                call.event);
         assertNotNull(call.r);
         assertNull(call.old);
         assertEquals(0, call.position);
@@ -1156,7 +1159,7 @@
         assertEquals(0, call.r.getSbn().getId());
         assertEquals(tag, call.r.getSbn().getTag());
         assertNotNull(call.r.getSbn().getInstanceId());
-        assertEquals(0, call.r.getSbn().getInstanceId().getId());
+        assertEquals(0, call.getInstanceId());  // Fake instance IDs are assigned in order
     }
 
     @Test
@@ -1174,18 +1177,18 @@
         waitForIdle();
         assertEquals(2, mNotificationRecordLogger.getCalls().size());
 
-        assertTrue(mNotificationRecordLogger.get(0).shouldLog());
+        assertTrue(mNotificationRecordLogger.get(0).shouldLog);
         assertEquals(
-                NotificationRecordLogger.NotificationReportedEvents.NOTIFICATION_POSTED,
-                mNotificationRecordLogger.get(0).getUiEvent());
-        assertEquals(0, mNotificationRecordLogger.get(0).r.getSbn().getInstanceId().getId());
+                NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
+                mNotificationRecordLogger.get(0).event);
+        assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
 
-        assertTrue(mNotificationRecordLogger.get(1).shouldLog());
+        assertTrue(mNotificationRecordLogger.get(1).shouldLog);
         assertEquals(
-                NotificationRecordLogger.NotificationReportedEvents.NOTIFICATION_UPDATED,
-                mNotificationRecordLogger.get(1).getUiEvent());
+                NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED,
+                mNotificationRecordLogger.get(1).event);
         // Instance ID doesn't change on update of an active notification
-        assertEquals(0, mNotificationRecordLogger.get(1).r.getSbn().getInstanceId().getId());
+        assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId());
     }
 
     @Test
@@ -1197,8 +1200,8 @@
                 generateNotificationRecord(null).getNotification(), 0);
         waitForIdle();
         assertEquals(2, mNotificationRecordLogger.getCalls().size());
-        assertTrue(mNotificationRecordLogger.get(0).shouldLog());
-        assertFalse(mNotificationRecordLogger.get(1).shouldLog());
+        assertTrue(mNotificationRecordLogger.get(0).shouldLog);
+        assertFalse(mNotificationRecordLogger.get(1).shouldLog);
     }
 
     @Test
@@ -1213,20 +1216,37 @@
         waitForIdle();
         mBinderService.enqueueNotificationWithTag(PKG, PKG, tag, 0, notification, 0);
         waitForIdle();
-        assertEquals(2, mNotificationRecordLogger.getCalls().size());
+        assertEquals(3, mNotificationRecordLogger.getCalls().size());
 
-        assertTrue(mNotificationRecordLogger.get(0).shouldLog());
         assertEquals(
-                NotificationRecordLogger.NotificationReportedEvents.NOTIFICATION_POSTED,
-                mNotificationRecordLogger.get(0).getUiEvent());
-        assertEquals(0, mNotificationRecordLogger.get(0).r.getSbn().getInstanceId().getId());
+                NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
+                mNotificationRecordLogger.get(0).event);
+        assertTrue(mNotificationRecordLogger.get(0).shouldLog);
+        assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
 
-        assertTrue(mNotificationRecordLogger.get(1).shouldLog());
+        assertEquals(REASON_APP_CANCEL, mNotificationRecordLogger.get(1).reason);
         assertEquals(
-                NotificationRecordLogger.NotificationReportedEvents.NOTIFICATION_POSTED,
-                mNotificationRecordLogger.get(1).getUiEvent());
+                NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_APP_CANCEL,
+                mNotificationRecordLogger.get(1).event);
+        assertTrue(mNotificationRecordLogger.get(1).shouldLog);
+        assertEquals(0, mNotificationRecordLogger.get(1).getInstanceId());
+
+        assertEquals(
+                NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED,
+                mNotificationRecordLogger.get(2).event);
+        assertTrue(mNotificationRecordLogger.get(2).shouldLog);
         // New instance ID because notification was canceled before re-post
-        assertEquals(1, mNotificationRecordLogger.get(1).r.getSbn().getInstanceId().getId());
+        assertEquals(1, mNotificationRecordLogger.get(2).getInstanceId());
+    }
+
+    @Test
+    public void testCancelNonexistentNotification() throws Exception {
+        mBinderService.cancelNotificationWithTag(PKG, PKG,
+                "testCancelNonexistentNotification", 0, 0);
+        waitForIdle();
+        // The notification record logger doesn't even get called when a nonexistent notification
+        // is cancelled, because that happens very frequently and is not interesting.
+        assertEquals(0, mNotificationRecordLogger.getCalls().size());
     }
 
     @Test
@@ -3368,6 +3388,17 @@
         waitForIdle();
 
         assertEquals(NotificationStats.DISMISSAL_AOD, r.getStats().getDismissalSurface());
+
+        // Using mService.addNotification() does not generate a NotificationRecordLogger log,
+        // so we only get the cancel notification.
+        assertEquals(1, mNotificationRecordLogger.getCalls().size());
+
+        assertEquals(REASON_CANCEL, mNotificationRecordLogger.get(0).reason);
+        assertEquals(
+                NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_AOD,
+                mNotificationRecordLogger.get(0).event);
+        assertTrue(mNotificationRecordLogger.get(0).shouldLog);
+        assertEquals(0, mNotificationRecordLogger.get(0).getInstanceId());
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
index 5c1487f..b120dbe 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerFake.java
@@ -16,6 +16,8 @@
 
 package com.android.server.notification;
 
+import com.android.internal.logging.UiEventLogger;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -24,18 +26,27 @@
  */
 class NotificationRecordLoggerFake implements NotificationRecordLogger {
     static class CallRecord extends NotificationRecordPair {
-        public int position, buzzBeepBlink;
+        static final int INVALID = -1;
+        public int position = INVALID, buzzBeepBlink = INVALID, reason = INVALID;
+        public boolean shouldLog;
+        public UiEventLogger.UiEventEnum event;
         CallRecord(NotificationRecord r, NotificationRecord old, int position,
                 int buzzBeepBlink) {
             super(r, old);
+
             this.position = position;
             this.buzzBeepBlink = buzzBeepBlink;
+            shouldLog = shouldLog(buzzBeepBlink);
+            event = NotificationReportedEvent.fromRecordPair(this);
         }
-        boolean shouldLog() {
-            return shouldLog(buzzBeepBlink);
+        CallRecord(NotificationRecord r, int reason, int dismissalSurface) {
+            super(r, null);
+            this.reason = reason;
+            shouldLog = true;
+            event = NotificationCancelledEvent.fromCancelReason(reason, dismissalSurface);
         }
     }
-    private List<CallRecord> mCalls = new ArrayList<CallRecord>();
+    private List<CallRecord> mCalls = new ArrayList<>();
 
     List<CallRecord> getCalls() {
         return mCalls;
@@ -50,4 +61,10 @@
             int position, int buzzBeepBlink) {
         mCalls.add(new CallRecord(r, old, position, buzzBeepBlink));
     }
+
+    @Override
+    public void logNotificationCancelled(NotificationRecord r, int reason, int dismissalSurface) {
+        mCalls.add(new CallRecord(r, reason, dismissalSurface));
+    }
+
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index ebe4ab9..a0ea729 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -29,6 +29,7 @@
 import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -140,6 +141,19 @@
     }
 
     @Test
+    public void testRemoveChildWithOverlayActivity() {
+        final ActivityRecord overlayActivity =
+                new ActivityBuilder(mService).setTask(mTask).build();
+        overlayActivity.setTaskOverlay(true);
+        final ActivityRecord overlayActivity2 =
+                new ActivityBuilder(mService).setTask(mTask).build();
+        overlayActivity2.setTaskOverlay(true);
+
+        mTask.removeChild(overlayActivity2, "test");
+        verify(mSupervisor, never()).removeTask(any(), anyBoolean(), anyBoolean(), any());
+    }
+
+    @Test
     public void testNoCleanupMovingActivityInSameStack() {
         final Task newTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack).build();
         mActivity.reparent(newTask, 0, null /*reason*/);
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 af04f76..135d005 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, null);
+                TEST_START_FLAGS, TEST_CALLING_PACKAGE);
 
         // 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 fa182d6..bab877e 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(), any(), anyInt(), anyInt(), anyInt(), 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(), any(), anyInt(), anyInt(), anyInt(), 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 c93dc97..eb84d0a 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, null, intent, null, aInfo /*aInfo*/, new Configuration(),
-                    null /* resultTo */, null /* resultWho */, 0 /* reqCode */,
-                    false /*componentSpecified*/, false /* rootVoiceInteraction */,
-                    mService.mStackSupervisor, options, null /* sourceRecord */);
+                    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/DisplayAreaProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
new file mode 100644
index 0000000..c1a1d5e
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaProviderTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.wm;
+
+import static com.android.server.wm.testing.Assert.assertThrows;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.platform.test.annotations.Presubmit;
+
+import org.hamcrest.Matchers;
+import org.junit.Assert;
+import org.junit.Test;
+
+@Presubmit
+public class DisplayAreaProviderTest {
+
+    @Test
+    public void testFromResources_emptyProvider() {
+        Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider("")),
+                Matchers.instanceOf(DisplayAreaPolicy.Default.Provider.class));
+    }
+
+    @Test
+    public void testFromResources_nullProvider() {
+        Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider(null)),
+                Matchers.instanceOf(DisplayAreaPolicy.Default.Provider.class));
+    }
+
+    @Test
+    public void testFromResources_customProvider() {
+        Assert.assertThat(DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider(
+                TestProvider.class.getName())), Matchers.instanceOf(TestProvider.class));
+    }
+
+    @Test
+    public void testFromResources_badProvider_notImplementingProviderInterface() {
+        assertThrows(IllegalStateException.class, () -> {
+            DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider(
+                    Object.class.getName()));
+        });
+    }
+
+    @Test
+    public void testFromResources_badProvider_doesntExist() {
+        assertThrows(IllegalStateException.class, () -> {
+            DisplayAreaPolicy.Provider.fromResources(resourcesWithProvider(
+                    "com.android.wmtests.nonexistent.Provider"));
+        });
+    }
+
+    private static Resources resourcesWithProvider(String provider) {
+        Resources mock = mock(Resources.class);
+        when(mock.getString(
+                com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider))
+                .thenReturn(provider);
+        return mock;
+    }
+
+    static class TestProvider implements DisplayAreaPolicy.Provider {
+
+        @Override
+        public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
+                DisplayArea.Root root, DisplayArea<? extends WindowContainer> imeContainer,
+                DisplayContent.TaskContainers taskContainers) {
+            throw new RuntimeException("test stub");
+        }
+    }
+}
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 9dbaa4c..e6291a4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -95,8 +95,6 @@
 
     @Before
     public void setUp() throws Exception {
-        updateDisplayFrames();
-
         mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
         // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from
         // changing those frames.
@@ -106,6 +104,8 @@
         attrs.width = MATCH_PARENT;
         attrs.height = MATCH_PARENT;
         attrs.format = PixelFormat.TRANSLUCENT;
+
+        updateDisplayFrames();
     }
 
     @After
@@ -126,6 +126,7 @@
 
     private void updateDisplayFrames() {
         mFrames = createDisplayFrames();
+        mDisplayContent.mDisplayFrames = mFrames;
     }
 
     private DisplayFrames createDisplayFrames() {
@@ -180,63 +181,6 @@
     }
 
     @Test
-    public void layoutWindowLw_appDrawsBars() {
-        assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
-        mWindow.mAttrs.flags = FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        mWindow.mAttrs.setFitInsetsTypes(Type.systemBars());
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
-        assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_forceAppDrawBars() {
-        assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
-        mWindow.mAttrs.privateFlags = PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
-        mWindow.mAttrs.setFitInsetsTypes(Type.systemBars());
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
-        assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
-    }
-
-    @Test
-    public void layoutWindowLw_onlyDrawBottomBar() {
-        assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
-
-        mWindow.mAttrs.flags = FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
-        mWindow.mAttrs.setFitInsetsTypes(Type.systemBars());
-        addWindow(mWindow);
-
-        mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
-        mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
-        assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
-        assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
-    }
-
-    @Test
     public void layoutWindowLw_fitAllSides() {
         assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
 
@@ -273,7 +217,7 @@
     }
 
     @Test
-    public void layoutWindowLw_fitMax() {
+    public void layoutWindowLw_fitInsetsIgnoringVisibility() {
         assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
 
         final InsetsState state =
@@ -295,7 +239,7 @@
     }
 
     @Test
-    public void layoutWindowLw_fitNonMax() {
+    public void layoutWindowLw_fitInsetsNotIgnoringVisibility() {
         assumeTrue(ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL);
 
         final InsetsState state =
@@ -398,6 +342,7 @@
 
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         addWindow(mWindow);
 
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -416,6 +361,7 @@
 
         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_NEVER;
         addWindow(mWindow);
 
@@ -435,6 +381,7 @@
 
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
         mWindow.mAttrs.setFitInsetsTypes(
                 mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars());
@@ -456,6 +403,7 @@
 
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
         mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow)
                 .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
@@ -477,6 +425,7 @@
 
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_FULLSCREEN;
         mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow)
                 .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
@@ -501,6 +450,7 @@
 
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         addWindow(mWindow);
 
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -521,6 +471,7 @@
 
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         addWindow(mWindow);
 
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
@@ -541,6 +492,7 @@
 
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
         mWindow.mAttrs.setFitInsetsTypes(
                 mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars());
@@ -581,6 +533,7 @@
 
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.subtreeSystemUiVisibility |= SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
         mWindow.mAttrs.setFitInsetsTypes(
                 mWindow.mAttrs.getFitInsetsTypes() & ~Type.statusBars());
@@ -603,6 +556,7 @@
 
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_RESIZE;
         addWindow(mWindow);
 
@@ -625,6 +579,7 @@
     public void layoutWindowLw_withForwardInset_SoftInputAdjustNothing() {
         mWindow.mAttrs.flags =
                 FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
         mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING;
         addWindow(mWindow);
 
@@ -849,7 +804,6 @@
         final String prefix = "";
         final InsetsState simulatedInsetsState = new InsetsState();
         final DisplayFrames simulatedDisplayFrames = createDisplayFrames();
-        mDisplayContent.mDisplayFrames = mFrames;
         mDisplayPolicy.beginLayoutLw(mFrames, uiMode);
         mDisplayContent.getInsetsStateController().onPostLayout();
         mDisplayPolicy.simulateLayoutDisplay(simulatedDisplayFrames, simulatedInsetsState, uiMode);
@@ -867,6 +821,7 @@
         // Exclude comparing IME insets because currently the simulated layout only focuses on the
         // insets from status bar and navigation bar.
         realInsetsState.removeSource(InsetsState.ITYPE_IME);
+        realInsetsState.removeSource(InsetsState.ITYPE_CAPTION_BAR);
         realInsetsState.dump(prefix, new PrintWriter(realInsetsDump));
         final StringWriter simulatedInsetsDump = new StringWriter();
         simulatedInsetsState.dump(prefix, new PrintWriter(simulatedInsetsDump));
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index e712255..eae007d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -29,12 +29,13 @@
 import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
 
 import android.platform.test.annotations.Presubmit;
 import android.util.IntArray;
@@ -122,13 +123,13 @@
     // TODO: adjust this test if we pretend to the app that it's still able to control it.
     @Test
     public void testControlsForDispatch_forceStatusBarVisible() {
-        addWindow(TYPE_STATUS_BAR, "topBar").mAttrs.privateFlags |=
+        addWindow(TYPE_STATUS_BAR, "statusBar").mAttrs.privateFlags |=
                 PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
         addWindow(TYPE_NAVIGATION_BAR, "navBar");
 
         final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
 
-        // The app must not control the top bar.
+        // The app must not control the status bar.
         assertNotNull(controls);
         assertEquals(1, controls.length);
     }
@@ -137,6 +138,7 @@
     public void testControlsForDispatch_statusBarForceShowNavigation() {
         addWindow(TYPE_NOTIFICATION_SHADE, "notificationShade").mAttrs.privateFlags |=
                 PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
+        addWindow(TYPE_STATUS_BAR, "statusBar");
         addWindow(TYPE_NAVIGATION_BAR, "navBar");
 
         final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
@@ -169,7 +171,8 @@
                 .getControllableInsetProvider().getSource().setVisible(false);
         final WindowState app = addWindow(TYPE_APPLICATION, "app");
 
-        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
+        final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
+        doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(app);
         policy.showTransient(
                 IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
@@ -184,7 +187,7 @@
     }
 
     @Test
-    public void testShowTransientBars_topCanBeTransient_appGetsTopFakeControl() {
+    public void testShowTransientBars_statusBarCanBeTransient_appGetsStatusBarFakeControl() {
         // Adding app window before setting source visibility is to prevent the visibility from
         // being cleared by InsetsSourceProvider.updateVisibility.
         final WindowState app = addWindow(TYPE_APPLICATION, "app");
@@ -194,14 +197,15 @@
         addWindow(TYPE_NAVIGATION_BAR, "navBar")
                 .getControllableInsetProvider().getSource().setVisible(true);
 
-        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
+        final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
+        doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(app);
         policy.showTransient(
                 IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
         final InsetsSourceControl[] controls =
                 mDisplayContent.getInsetsStateController().getControlsForDispatch(app);
 
-        // The app must get the fake control of the top bar, and must get the real control of the
+        // The app must get the fake control of the status bar, and must get the real control of the
         // navigation bar.
         assertEquals(2, controls.length);
         for (int i = controls.length - 1; i >= 0; i--) {
@@ -222,7 +226,8 @@
                 .getControllableInsetProvider().getSource().setVisible(false);
         final WindowState app = addWindow(TYPE_APPLICATION, "app");
 
-        final InsetsPolicy policy = mDisplayContent.getInsetsPolicy();
+        final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
+        doNothing().when(policy).startAnimation(anyBoolean(), any());
         policy.updateBarControlTarget(app);
         policy.showTransient(
                 IntArray.wrap(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}));
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 39cdd2c..5cf1fbb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -20,7 +20,9 @@
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
@@ -33,14 +35,14 @@
 import android.view.InsetsState;
 import android.view.test.InsetsModeSession;
 
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-
 @SmallTest
 @FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
 @Presubmit
@@ -88,6 +90,10 @@
         final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+
+        // IME cannot be the IME target.
+        ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+
         getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
         getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null);
         getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null);
@@ -98,6 +104,10 @@
     public void testImeForDispatch() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
+
+        // IME cannot be the IME target.
+        ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
+
         InsetsSourceProvider statusBarProvider =
                 getController().getSourceProvider(ITYPE_STATUS_BAR);
         statusBarProvider.setWindow(statusBar, null, ((displayFrames, windowState, rect) ->
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 f517881..e507508 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 AppOpsManager aos = mock(AppOpsManager.class);
-            doReturn(aos).when(this).getAppOpsManager();
+            final AppOpsService aos = mock(AppOpsService.class);
+            doReturn(aos).when(this).getAppOpsService();
             // Make sure permission checks aren't overridden.
-            doReturn(AppOpsManager.MODE_DEFAULT).when(aos).noteOpNoThrow(anyInt(), anyInt(),
-                    anyString(), nullable(String.class), nullable(String.class));
+            doReturn(AppOpsManager.MODE_DEFAULT).when(aos).noteOperation(anyInt(), anyInt(),
+                    anyString(), nullable(String.class), anyBoolean(), nullable(String.class));
 
             // UserManagerService
             final UserManagerService ums = mock(UserManagerService.class);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index f7aa3cc..0312df6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -34,14 +34,21 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
+import android.app.PictureInPictureParams;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -49,6 +56,8 @@
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.util.ArrayMap;
+import android.content.pm.ActivityInfo;
+import android.util.Rational;
 import android.view.Display;
 import android.view.ITaskOrganizer;
 import android.view.IWindowContainer;
@@ -384,11 +393,19 @@
         RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
                 mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
 
+        final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
+                mDisplayContent.mDisplayId, null /* activityTypes */).size();
+
         final ActivityStack stack = createTaskStackOnDisplay(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
         final ActivityStack stack2 = createTaskStackOnDisplay(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
 
+        // Check getRootTasks works
+        List<RunningTaskInfo> roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks(
+                mDisplayContent.mDisplayId, null /* activityTypes */);
+        assertEquals(initialRootTaskCount + 2, roots.size());
+
         lastReportedTiles.clear();
         WindowContainerTransaction wct = new WindowContainerTransaction();
         wct.reparent(stack.mRemoteToken, info1.token, true /* onTop */);
@@ -415,11 +432,18 @@
 
         // Check the getChildren call
         List<RunningTaskInfo> children =
-                mWm.mAtmService.mTaskOrganizerController.getChildTasks(info1.token);
+                mWm.mAtmService.mTaskOrganizerController.getChildTasks(info1.token,
+                        null /* activityTypes */);
         assertEquals(2, children.size());
-        children = mWm.mAtmService.mTaskOrganizerController.getChildTasks(info2.token);
+        children = mWm.mAtmService.mTaskOrganizerController.getChildTasks(info2.token,
+                null /* activityTypes */);
         assertEquals(0, children.size());
 
+        // Check that getRootTasks doesn't include children of tiles
+        roots = mWm.mAtmService.mTaskOrganizerController.getRootTasks(mDisplayContent.mDisplayId,
+                null /* activityTypes */);
+        assertEquals(initialRootTaskCount, roots.size());
+
         lastReportedTiles.clear();
         wct = new WindowContainerTransaction();
         wct.reorder(stack2.mRemoteToken, true /* onTop */);
@@ -483,4 +507,76 @@
         verify(transactionListener)
             .transactionReady(anyInt(), any());
     }
+
+    class StubOrganizer extends ITaskOrganizer.Stub {
+        RunningTaskInfo mInfo;
+
+        @Override
+        public void taskAppeared(RunningTaskInfo info) {
+            mInfo = info;
+        }
+        @Override
+        public void taskVanished(IWindowContainer wc) {
+        }
+        @Override
+        public void transactionReady(int id, SurfaceControl.Transaction t) {
+        }
+        @Override
+        public void onTaskInfoChanged(RunningTaskInfo info) {
+        }
+    };
+
+    private ActivityRecord makePipableActivity() {
+        final ActivityRecord record = createActivityRecord(mDisplayContent,
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+        record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
+        spyOn(record);
+        doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean());
+        return record;
+    }
+
+    @Test
+    public void testEnterPipParams() {
+        final StubOrganizer o = new StubOrganizer();
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o, WINDOWING_MODE_PINNED);
+        final ActivityRecord record = makePipableActivity();
+
+        final PictureInPictureParams p =
+            new PictureInPictureParams.Builder().setAspectRatio(new Rational(1, 2)).build();
+        assertTrue(mWm.mAtmService.enterPictureInPictureMode(record.token, p));
+        waitUntilHandlersIdle();
+        assertNotNull(o.mInfo);
+        assertNotNull(o.mInfo.pictureInPictureParams);
+    }
+
+    @Test
+    public void testChangePipParams() {
+        class ChangeSavingOrganizer extends StubOrganizer {
+            RunningTaskInfo mChangedInfo;
+            @Override
+            public void onTaskInfoChanged(RunningTaskInfo info) {
+                mChangedInfo = info;
+            }
+        }
+        ChangeSavingOrganizer o = new ChangeSavingOrganizer();
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o, WINDOWING_MODE_PINNED);
+
+        final ActivityRecord record = makePipableActivity();
+        final PictureInPictureParams p =
+            new PictureInPictureParams.Builder().setAspectRatio(new Rational(1, 2)).build();
+        assertTrue(mWm.mAtmService.enterPictureInPictureMode(record.token, p));
+        waitUntilHandlersIdle();
+        assertNotNull(o.mInfo);
+        assertNotNull(o.mInfo.pictureInPictureParams);
+
+        final PictureInPictureParams p2 =
+            new PictureInPictureParams.Builder().setAspectRatio(new Rational(3, 4)).build();
+        mWm.mAtmService.setPictureInPictureParams(record.token, p2);
+        waitUntilHandlersIdle();
+        assertNotNull(o.mChangedInfo);
+        assertNotNull(o.mChangedInfo.pictureInPictureParams);
+        final Rational ratio = o.mChangedInfo.pictureInPictureParams.getAspectRatioRational();
+        assertEquals(3, ratio.getNumerator());
+        assertEquals(4, ratio.getDenominator());
+    }
 }
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 feadd0a..ebf14d2 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, null, 0, false, false, false, 0,
+                0, false, null, 0, 0, 0, 0, 0, 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, String callingFeatureId, int resizeMode,
+                int callingUid, String callingPackage, int resizeMode,
                 boolean supportsPictureInPicture,
                 boolean realActivitySuspended, boolean userSetupComplete, int minWidth,
                 int minHeight, ActivityStack stack) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 8378d8e..d5eec33 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -747,8 +747,7 @@
         }
 
         @Override
-        public int startVoiceActivity(IBinder token, Intent intent, String resolvedType,
-                String callingFeatureId) {
+        public int startVoiceActivity(IBinder token, Intent intent, String resolvedType) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "startVoiceActivity without running voice interaction service");
@@ -758,8 +757,8 @@
                 final int callingUid = Binder.getCallingUid();
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    return mImpl.startVoiceActivityLocked(callingFeatureId, callingPid, callingUid,
-                            token, intent, resolvedType);
+                    return mImpl.startVoiceActivityLocked(callingPid, callingUid, token,
+                            intent, resolvedType);
                 } finally {
                     Binder.restoreCallingIdentity(caller);
                 }
@@ -767,8 +766,7 @@
         }
 
         @Override
-        public int startAssistantActivity(IBinder token, Intent intent, String resolvedType,
-                String callingFeatureId) {
+        public int startAssistantActivity(IBinder token, Intent intent, String resolvedType) {
             synchronized (this) {
                 if (mImpl == null) {
                     Slog.w(TAG, "startAssistantActivity without running voice interaction service");
@@ -778,8 +776,8 @@
                 final int callingUid = Binder.getCallingUid();
                 final long caller = Binder.clearCallingIdentity();
                 try {
-                    return mImpl.startAssistantActivityLocked(callingFeatureId, callingPid,
-                            callingUid, token, intent, resolvedType);
+                    return mImpl.startAssistantActivityLocked(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 a62b03c..a1210cf 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(@Nullable String callingFeatureId, int callingPid,
-            int callingUid, IBinder token, Intent intent, String resolvedType) {
+    public int startVoiceActivityLocked(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(), callingFeatureId,
-                    callingPid, callingUid, intent, resolvedType, mActiveSession.mSession,
-                    mActiveSession.mInteractor, 0, null, null, mUser);
+            return mAtm.startVoiceActivity(mComponent.getPackageName(), 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(@Nullable String callingFeatureId, int callingPid,
-            int callingUid, IBinder token, Intent intent, String resolvedType) {
+    public int startAssistantActivityLocked(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(), callingFeatureId,
-                    callingPid, callingUid, intent, resolvedType, options.toBundle(), mUser);
+            return mAtm.startAssistantActivity(mComponent.getPackageName(), callingPid, callingUid,
+                    intent, resolvedType, options.toBundle(), mUser);
         } catch (RemoteException e) {
             throw new IllegalStateException("Unexpected remote error", e);
         }
diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
index 553bcff..e97cfaf 100644
--- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java
@@ -123,6 +123,14 @@
         return userContext.getContentResolver();
     }
 
+    private static boolean isUpdatedSystemApp(ApplicationInfo ai) {
+        if ((ai.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+            return true;
+        }
+
+        return false;
+    }
+
     /**
      * Disable carrier apps until they are privileged
      * Must be public b/c framework unit tests can't access package-private methods.
@@ -137,7 +145,7 @@
         PackageManager packageManager = context.getPackageManager();
         PermissionManager permissionManager =
                 (PermissionManager) context.getSystemService(Context.PERMISSION_SERVICE);
-        List<ApplicationInfo> candidates = getDefaultNotUpdatedCarrierAppCandidatesHelper(
+        List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(
                 userId, systemCarrierAppsDisabledUntilUsed, context);
         if (candidates == null || candidates.isEmpty()) {
             return;
@@ -176,7 +184,7 @@
                 if (hasPrivileges) {
                     // Only update enabled state for the app on /system. Once it has been
                     // updated we shouldn't touch it.
-                    if (enabledSetting
+                    if (!isUpdatedSystemApp(ai) && enabledSetting
                             == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                             || enabledSetting
                             == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
@@ -230,7 +238,7 @@
                 } else {  // No carrier privileges
                     // Only update enabled state for the app on /system. Once it has been
                     // updated we shouldn't touch it.
-                    if (enabledSetting
+                    if (!isUpdatedSystemApp(ai) && enabledSetting
                             == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                             && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
                         Log.i(TAG, "Update state(" + packageName
@@ -361,29 +369,6 @@
         return apps;
     }
 
-    private static List<ApplicationInfo> getDefaultNotUpdatedCarrierAppCandidatesHelper(
-            int userId, ArraySet<String> systemCarrierAppsDisabledUntilUsed, Context context) {
-        if (systemCarrierAppsDisabledUntilUsed == null) {
-            return null;
-        }
-
-        int size = systemCarrierAppsDisabledUntilUsed.size();
-        if (size == 0) {
-            return null;
-        }
-
-        List<ApplicationInfo> apps = new ArrayList<>(size);
-        for (int i = 0; i < size; i++) {
-            String packageName = systemCarrierAppsDisabledUntilUsed.valueAt(i);
-            ApplicationInfo ai =
-                    getApplicationInfoIfNotUpdatedSystemApp(userId, packageName, context);
-            if (ai != null) {
-                apps.add(ai);
-            }
-        }
-        return apps;
-    }
-
     private static Map<String, List<ApplicationInfo>> getDefaultCarrierAssociatedAppsHelper(
             int userId, ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed,
             Context context) {
@@ -395,11 +380,11 @@
                     systemCarrierAssociatedAppsDisabledUntilUsed.valueAt(i);
             for (int j = 0; j < associatedAppPackages.size(); j++) {
                 ApplicationInfo ai =
-                        getApplicationInfoIfNotUpdatedSystemApp(
+                        getApplicationInfoIfSystemApp(
                                 userId, associatedAppPackages.get(j), context);
                 // Only update enabled state for the app on /system. Once it has been updated we
                 // shouldn't touch it.
-                if (ai != null) {
+                if (ai != null && !isUpdatedSystemApp(ai)) {
                     List<ApplicationInfo> appList = associatedApps.get(carrierAppPackage);
                     if (appList == null) {
                         appList = new ArrayList<>();
@@ -413,26 +398,6 @@
     }
 
     @Nullable
-    private static ApplicationInfo getApplicationInfoIfNotUpdatedSystemApp(
-            int userId, String packageName, Context context) {
-        try {
-            ApplicationInfo ai = context.createContextAsUser(UserHandle.of(userId), 0)
-                    .getPackageManager()
-                    .getApplicationInfo(packageName,
-                            PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
-                                    | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
-                                    | PackageManager.MATCH_SYSTEM_ONLY
-                                    | PackageManager.MATCH_FACTORY_ONLY);
-            if (ai != null) {
-                return ai;
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.w(TAG, "Could not reach PackageManager", e);
-        }
-        return null;
-    }
-
-    @Nullable
     private static ApplicationInfo getApplicationInfoIfSystemApp(
             int userId, String packageName, Context context) {
         try {
diff --git a/tests/BootImageProfileTest/OWNERS b/tests/BootImageProfileTest/OWNERS
new file mode 100644
index 0000000..657b3f2
--- /dev/null
+++ b/tests/BootImageProfileTest/OWNERS
@@ -0,0 +1,4 @@
+mathieuc@google.com
+calin@google.com
+yawanng@google.com
+sehr@google.com
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
index 5904916..1361df3 100644
--- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
@@ -321,8 +321,7 @@
                 }
 
                 mAtm.startActivityAndWait(null,
-                        getInstrumentation().getContext().getBasePackageName(),
-                        getInstrumentation().getContext().getFeatureId(), mLaunchIntent,
+                        getInstrumentation().getContext().getBasePackageName(), mLaunchIntent,
                         mimeType, null, null, 0, mLaunchIntent.getFlags(), null, null,
                         UserHandle.USER_CURRENT_OR_SELF);
             } catch (RemoteException e) {
diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp
index 74dfde8..342c47d 100644
--- a/tests/PlatformCompatGating/Android.bp
+++ b/tests/PlatformCompatGating/Android.bp
@@ -18,6 +18,7 @@
     name: "PlatformCompatGating",
     // Only compile source java files in this apk.
     srcs: ["src/**/*.java"],
+    test_suites: ["device-tests"],
     static_libs: [
         "junit",
         "androidx.test.runner",
diff --git a/tests/net/Android.bp b/tests/net/Android.bp
index b2f384a..124b660 100644
--- a/tests/net/Android.bp
+++ b/tests/net/Android.bp
@@ -8,7 +8,6 @@
         "libbacktrace",
         "libbase",
         "libbinder",
-        "libbinderthreadstate",
         "libbpf",
         "libbpf_android",
         "libc++",
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 2af118c..5aa32f8 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -391,6 +391,7 @@
   manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);
   manifest_action["queries"]["package"].Action(RequiredNameIsJavaPackage);
   manifest_action["queries"]["intent"] = intent_filter_action;
+  manifest_action["queries"]["provider"].Action(RequiredAndroidAttribute("authorities"));
   // TODO: more complicated component name tag
 
   manifest_action["key-sets"]["key-set"]["public-key"];
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 221f644..1c330e2 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -270,4 +270,10 @@
     void setScanThrottleEnabled(boolean enable);
 
     boolean isScanThrottleEnabled();
+
+    Map getAllMatchingPasspointProfilesForScanResults(in List<ScanResult> scanResult);
+
+    void setAutoWakeupEnabled(boolean enable);
+
+    boolean isAutoWakeupEnabled();
 }
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 7c3d0b9..0cce23d 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -951,7 +951,7 @@
      * Indicate whether the network is trusted or not. Networks are considered trusted
      * if the user explicitly allowed this network connection.
      * This bit can be used by suggestion network, see
-     * {@link WifiNetworkSuggestion.Builder#setUnTrusted(boolean)}
+     * {@link WifiNetworkSuggestion.Builder#setUntrusted(boolean)}
      * @hide
      */
     public boolean trusted;
@@ -2112,7 +2112,8 @@
         return !TextUtils.isEmpty(FQDN)
                 && !TextUtils.isEmpty(providerFriendlyName)
                 && enterpriseConfig != null
-                && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE;
+                && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE
+                && !TextUtils.isEmpty(mPasspointUniqueId);
     }
 
     /**
@@ -2494,12 +2495,17 @@
      */
     @NonNull
     public String getKey() {
-        String key = providerFriendlyName == null
-                ? getSsidAndSecurityTypeString()
-                : FQDN + KeyMgmt.strings[KeyMgmt.WPA_EAP];
+        // Passpoint ephemeral networks have their unique identifier set. Return it as is to be
+        // able to match internally.
+        if (mPasspointUniqueId != null) {
+            return mPasspointUniqueId;
+        }
+
+        String key = getSsidAndSecurityTypeString();
         if (!shared) {
             key += "-" + UserHandle.getUserHandleForUid(creatorUid).getIdentifier();
         }
+
         return key;
     }
 
@@ -2754,6 +2760,7 @@
             requirePMF = source.requirePMF;
             updateIdentifier = source.updateIdentifier;
             carrierId = source.carrierId;
+            mPasspointUniqueId = source.mPasspointUniqueId;
         }
     }
 
@@ -2826,6 +2833,7 @@
         dest.writeInt(osu ? 1 : 0);
         dest.writeLong(randomizedMacExpirationTimeMs);
         dest.writeInt(carrierId);
+        dest.writeString(mPasspointUniqueId);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -2900,6 +2908,7 @@
                 config.osu = in.readInt() != 0;
                 config.randomizedMacExpirationTimeMs = in.readLong();
                 config.carrierId = in.readInt();
+                config.mPasspointUniqueId = in.readString();
                 return config;
             }
 
@@ -2907,4 +2916,28 @@
                 return new WifiConfiguration[size];
             }
         };
+
+    /**
+     * Passpoint Unique identifier
+     * @hide
+     */
+    private String mPasspointUniqueId = null;
+
+    /**
+     * Set the Passpoint unique identifier
+     * @param uniqueId Passpoint unique identifier to be set
+     * @hide
+     */
+    public void setPasspointUniqueId(String uniqueId) {
+        mPasspointUniqueId = uniqueId;
+    }
+
+    /**
+     * Set the Passpoint unique identifier
+     * @hide
+     */
+    public String getPasspointUniqueId() {
+        return mPasspointUniqueId;
+    }
+
 }
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 24b2a8e..0c306b4 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -291,6 +291,11 @@
      */
     private boolean mMeteredHint;
 
+    /**
+     * Passpoint unique key
+     */
+    private String mPasspointUniqueId;
+
     /** @hide */
     @UnsupportedAppUsage
     public WifiInfo() {
@@ -322,6 +327,7 @@
         setRequestingPackageName(null);
         setFQDN(null);
         setProviderFriendlyName(null);
+        setPasspointUniqueId(null);
         txBad = 0;
         txSuccess = 0;
         rxSuccess = 0;
@@ -370,6 +376,7 @@
             mWifiStandard = source.mWifiStandard;
             mMaxSupportedTxLinkSpeed = source.mMaxSupportedTxLinkSpeed;
             mMaxSupportedRxLinkSpeed = source.mMaxSupportedRxLinkSpeed;
+            mPasspointUniqueId = source.mPasspointUniqueId;
         }
     }
 
@@ -977,6 +984,7 @@
         dest.writeInt(mWifiStandard);
         dest.writeInt(mMaxSupportedTxLinkSpeed);
         dest.writeInt(mMaxSupportedRxLinkSpeed);
+        dest.writeString(mPasspointUniqueId);
     }
 
     /** Implement the Parcelable interface {@hide} */
@@ -1021,6 +1029,7 @@
                 info.mWifiStandard = in.readInt();
                 info.mMaxSupportedTxLinkSpeed = in.readInt();
                 info.mMaxSupportedRxLinkSpeed = in.readInt();
+                info.mPasspointUniqueId = in.readString();
                 return info;
             }
 
@@ -1028,4 +1037,24 @@
                 return new WifiInfo[size];
             }
         };
+
+    /**
+     * Set the Passpoint unique identifier for the current connection
+     *
+     * @param passpointUniqueId Unique identifier
+     * @hide
+     */
+    public void setPasspointUniqueId(@Nullable String passpointUniqueId) {
+        mPasspointUniqueId = passpointUniqueId;
+    }
+
+    /**
+     * Get the Passpoint unique identifier for the current connection
+     *
+     * @return Passpoint unique identifier
+     * @hide
+     */
+    public @Nullable String getPasspointUniqueId() {
+        return mPasspointUniqueId;
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 5ccc3aa..b6f4490 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1400,8 +1400,7 @@
         List<Pair<WifiConfiguration, Map<Integer, List<ScanResult>>>> configs = new ArrayList<>();
         try {
             Map<String, Map<Integer, List<ScanResult>>> results =
-                    mService.getAllMatchingFqdnsForScanResults(
-                            scanResults);
+                    mService.getAllMatchingPasspointProfilesForScanResults(scanResults);
             if (results.isEmpty()) {
                 return configs;
             }
@@ -1409,8 +1408,8 @@
                     mService.getWifiConfigsForPasspointProfiles(
                             new ArrayList<>(results.keySet()));
             for (WifiConfiguration configuration : wifiConfigurations) {
-                Map<Integer, List<ScanResult>> scanResultsPerNetworkType = results.get(
-                        configuration.FQDN);
+                Map<Integer, List<ScanResult>> scanResultsPerNetworkType =
+                        results.get(configuration.getKey());
                 if (scanResultsPerNetworkType != null) {
                     configs.add(Pair.create(configuration, scanResultsPerNetworkType));
                 }
@@ -1962,9 +1961,11 @@
      * for connecting to Passpoint networks that are operated by the Passpoint
      * service provider specified in the configuration.
      *
-     * Each configuration is uniquely identified by its FQDN (Fully Qualified Domain
-     * Name).  In the case when there is an existing configuration with the same
-     * FQDN, the new configuration will replace the existing configuration.
+     * Each configuration is uniquely identified by a unique key which depends on the contents of
+     * the configuration. This allows the caller to install multiple profiles with the same FQDN
+     * (Fully qualified domain name). Therefore, in order to update an existing profile, it is
+     * first required to remove it using {@link WifiManager#removePasspointConfiguration(String)}.
+     * Otherwise, a new profile will be added with both configuration.
      *
      * @param config The Passpoint configuration to be added
      * @throws IllegalArgumentException if configuration is invalid or Passpoint is not enabled on
@@ -6162,4 +6163,50 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Enable/disable wifi auto wakeup feature.
+     *
+     * <p>
+     * The feature is described in
+     * <a href="Wi-Fi Turn on automatically">
+     * https://source.android.com/devices/tech/connect/wifi-infrastructure
+     * #turn_on_wi-fi_automatically
+     * </a>
+     *
+     * @param enable true to enable, false to disable.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+    public void setAutoWakeupEnabled(boolean enable) {
+        try {
+            mService.setAutoWakeupEnabled(enable);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Get the persisted Wi-Fi auto wakeup feature state. Defaults to false, unless changed by the
+     * user via Settings.
+     *
+     * <p>
+     * The feature is described in
+     * <a href="Wi-Fi Turn on automatically">
+     * https://source.android.com/devices/tech/connect/wifi-infrastructure
+     * #turn_on_wi-fi_automatically
+     * </a>
+     *
+     * @return true to indicate that wakeup feature is enabled, false to indicate that wakeup
+     * feature is disabled.
+     */
+    @RequiresPermission(ACCESS_WIFI_STATE)
+    public boolean isAutoWakeupEnabled() {
+        try {
+            return mService.isAutoWakeupEnabled();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 7201496..a854a4b 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -569,6 +569,7 @@
         private WifiConfiguration buildWifiConfigurationForPasspoint() {
             WifiConfiguration wifiConfiguration = new WifiConfiguration();
             wifiConfiguration.FQDN = mPasspointConfiguration.getHomeSp().getFqdn();
+            wifiConfiguration.setPasspointUniqueId(mPasspointConfiguration.getUniqueId());
             wifiConfiguration.priority = mPriority;
             wifiConfiguration.meteredOverride =
                     mIsMetered ? WifiConfiguration.METERED_OVERRIDE_METERED
@@ -804,7 +805,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(wifiConfiguration.SSID, wifiConfiguration.BSSID,
-                wifiConfiguration.allowedKeyManagement, wifiConfiguration.FQDN);
+                wifiConfiguration.allowedKeyManagement, wifiConfiguration.getKey());
     }
 
     /**
@@ -827,7 +828,8 @@
                 && TextUtils.equals(this.wifiConfiguration.BSSID, lhs.wifiConfiguration.BSSID)
                 && Objects.equals(this.wifiConfiguration.allowedKeyManagement,
                 lhs.wifiConfiguration.allowedKeyManagement)
-                && TextUtils.equals(this.wifiConfiguration.FQDN, lhs.wifiConfiguration.FQDN);
+                && TextUtils.equals(this.wifiConfiguration.getKey(),
+                lhs.wifiConfiguration.getKey());
     }
 
     @Override
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 615331f..9f58184 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -908,6 +908,9 @@
             throw new IllegalStateException("Credential or HomeSP are not initialized");
         }
 
-        return mHomeSp.getFqdn();
+        StringBuilder sb = new StringBuilder();
+        sb.append(String.format("%s_%x%x", mHomeSp.getFqdn(), mHomeSp.hashCode(),
+                mCredential.hashCode()));
+        return sb.toString();
     }
 }
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
index 49a76c3..a5de331 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
@@ -16,8 +16,8 @@
 
 package android.net.wifi.hotspot2.pps;
 
-import android.os.Parcelable;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -299,8 +299,10 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mFqdn, mFriendlyName, mIconUrl, mHomeNetworkIds, mMatchAllOis,
-                mMatchAnyOis, mOtherHomePartners, mRoamingConsortiumOis);
+        return Objects.hash(mFqdn, mFriendlyName, mIconUrl,
+                mHomeNetworkIds, Arrays.hashCode(mMatchAllOis),
+                Arrays.hashCode(mMatchAnyOis), Arrays.hashCode(mOtherHomePartners),
+                Arrays.hashCode(mRoamingConsortiumOis));
     }
 
     @Override
diff --git a/wifi/java/android/net/wifi/wificond/NativeScanResult.java b/wifi/java/android/net/wifi/wificond/NativeScanResult.java
index 7cc617d..bd99476 100644
--- a/wifi/java/android/net/wifi/wificond/NativeScanResult.java
+++ b/wifi/java/android/net/wifi/wificond/NativeScanResult.java
@@ -16,14 +16,21 @@
 
 package android.net.wifi.wificond;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.net.MacAddress;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -33,6 +40,8 @@
  */
 @SystemApi
 public final class NativeScanResult implements Parcelable {
+    private static final String TAG = "NativeScanResult";
+
     /** @hide */
     @VisibleForTesting
     public byte[] ssid;
@@ -53,7 +62,7 @@
     public long tsf;
     /** @hide */
     @VisibleForTesting
-    public int capability;
+    @BssCapabilityBits public int capability;
     /** @hide */
     @VisibleForTesting
     public boolean associated;
@@ -71,14 +80,17 @@
     }
 
     /**
-     * Returns raw bytes representing the MAC address (BSSID) of the AP represented by this scan
-     * result.
+     * Returns the MAC address (BSSID) of the AP represented by this scan result.
      *
-     * @return a byte array, possibly null or containing the incorrect number of bytes for a MAC
-     * address.
+     * @return a MacAddress or null on error.
      */
-    @NonNull public byte[] getBssid() {
-        return bssid;
+    @Nullable public MacAddress getBssid() {
+        try {
+            return MacAddress.fromBytes(bssid);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Illegal argument " + Arrays.toString(bssid), e);
+            return null;
+        }
     }
 
     /**
@@ -127,31 +139,103 @@
         return associated;
     }
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = {"BSS_CAPABILITY_"},
+            value = {BSS_CAPABILITY_ESS,
+                    BSS_CAPABILITY_IBSS,
+                    BSS_CAPABILITY_CF_POLLABLE,
+                    BSS_CAPABILITY_CF_POLL_REQUEST,
+                    BSS_CAPABILITY_PRIVACY,
+                    BSS_CAPABILITY_SHORT_PREAMBLE,
+                    BSS_CAPABILITY_PBCC,
+                    BSS_CAPABILITY_CHANNEL_AGILITY,
+                    BSS_CAPABILITY_SPECTRUM_MANAGEMENT,
+                    BSS_CAPABILITY_QOS,
+                    BSS_CAPABILITY_SHORT_SLOT_TIME,
+                    BSS_CAPABILITY_APSD,
+                    BSS_CAPABILITY_RADIO_MANAGEMENT,
+                    BSS_CAPABILITY_DSSS_OFDM,
+                    BSS_CAPABILITY_DELAYED_BLOCK_ACK,
+                    BSS_CAPABILITY_IMMEDIATE_BLOCK_ACK
+            })
+    public @interface BssCapabilityBits { }
+
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): ESS.
+     */
+    public static final int BSS_CAPABILITY_ESS = 0x1 << 0;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): IBSS.
+     */
+    public static final int BSS_CAPABILITY_IBSS = 0x1 << 1;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): CF Pollable.
+     */
+    public static final int BSS_CAPABILITY_CF_POLLABLE = 0x1 << 2;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): CF-Poll Request.
+     */
+    public static final int BSS_CAPABILITY_CF_POLL_REQUEST = 0x1 << 3;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Privacy.
+     */
+    public static final int BSS_CAPABILITY_PRIVACY = 0x1 << 4;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Short Preamble.
+     */
+    public static final int BSS_CAPABILITY_SHORT_PREAMBLE = 0x1 << 5;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): PBCC.
+     */
+    public static final int BSS_CAPABILITY_PBCC = 0x1 << 6;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Channel Agility.
+     */
+    public static final int BSS_CAPABILITY_CHANNEL_AGILITY = 0x1 << 7;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Spectrum Management.
+     */
+    public static final int BSS_CAPABILITY_SPECTRUM_MANAGEMENT = 0x1 << 8;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): QoS.
+     */
+    public static final int BSS_CAPABILITY_QOS = 0x1 << 9;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Short Slot Time.
+     */
+    public static final int BSS_CAPABILITY_SHORT_SLOT_TIME = 0x1 << 10;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): APSD.
+     */
+    public static final int BSS_CAPABILITY_APSD = 0x1 << 11;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Radio Management.
+     */
+    public static final int BSS_CAPABILITY_RADIO_MANAGEMENT = 0x1 << 12;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): DSSS-OFDM.
+     */
+    public static final int BSS_CAPABILITY_DSSS_OFDM = 0x1 << 13;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Delayed Block Ack.
+     */
+    public static final int BSS_CAPABILITY_DELAYED_BLOCK_ACK = 0x1 << 14;
+    /**
+     * BSS capability bit (see IEEE Std 802.11: 9.4.1.4): Immediate Block Ack.
+     */
+    public static final int BSS_CAPABILITY_IMMEDIATE_BLOCK_ACK = 0x1 << 15;
+
     /**
      *  Returns the capabilities of the AP repseresented by this scan result as advertised in the
      *  received probe response or beacon.
      *
-     *  This is a bit mask describing the capabilities of a BSS. See IEEE Std 802.11: 9.4.1.4:
-     *    Bit 0 - ESS
-     *    Bit 1 - IBSS
-     *    Bit 2 - CF Pollable
-     *    Bit 3 - CF-Poll Request
-     *    Bit 4 - Privacy
-     *    Bit 5 - Short Preamble
-     *    Bit 6 - PBCC
-     *    Bit 7 - Channel Agility
-     *    Bit 8 - Spectrum Management
-     *    Bit 9 - QoS
-     *    Bit 10 - Short Slot Time
-     *    Bit 11 - APSD
-     *    Bit 12 - Radio Measurement
-     *    Bit 13 - DSSS-OFDM
-     *    Bit 14 - Delayed Block Ack
-     *    Bit 15 - Immediate Block Ack
+     *  This is a bit mask describing the capabilities of a BSS. See IEEE Std 802.11: 9.4.1.4: one
+     *  of the {@code BSS_CAPABILITY_*} flags.
      *
      * @return a bit mask of capabilities.
      */
-    @NonNull public int getCapabilities() {
+    @BssCapabilityBits public int getCapabilities() {
         return capability;
     }
 
diff --git a/wifi/java/android/net/wifi/wificond/NativeWifiClient.java b/wifi/java/android/net/wifi/wificond/NativeWifiClient.java
index 916c115..9ad2a27 100644
--- a/wifi/java/android/net/wifi/wificond/NativeWifiClient.java
+++ b/wifi/java/android/net/wifi/wificond/NativeWifiClient.java
@@ -17,11 +17,13 @@
 package android.net.wifi.wificond;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.net.MacAddress;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Structure providing information about clients (STAs) associated with a SoftAp.
@@ -30,16 +32,21 @@
  */
 @SystemApi
 public final class NativeWifiClient implements Parcelable {
+    private final MacAddress mMacAddress;
+
     /**
-     * The raw bytes of the MAC address of the client (STA) represented by this object.
+     * The MAC address of the client (STA) represented by this object. The MAC address may be null
+     * in case of an error.
      */
-    @NonNull public final byte[] macAddress;
+    @Nullable public MacAddress getMacAddress() {
+        return mMacAddress;
+    }
 
     /**
      * Construct a native Wi-Fi client.
      */
-    public NativeWifiClient(@NonNull byte[] macAddress) {
-        this.macAddress = macAddress;
+    public NativeWifiClient(@Nullable MacAddress macAddress) {
+        this.mMacAddress = macAddress;
     }
 
     /** override comparator */
@@ -50,13 +57,13 @@
             return false;
         }
         NativeWifiClient other = (NativeWifiClient) rhs;
-        return Arrays.equals(macAddress, other.macAddress);
+        return Objects.equals(mMacAddress, other.mMacAddress);
     }
 
     /** override hash code */
     @Override
     public int hashCode() {
-        return Arrays.hashCode(macAddress);
+        return mMacAddress.hashCode();
     }
 
     /** implement Parcelable interface */
@@ -71,7 +78,7 @@
      */
     @Override
     public void writeToParcel(@NonNull Parcel out, int flags) {
-        out.writeByteArray(macAddress);
+        out.writeByteArray(mMacAddress.toByteArray());
     }
 
     /** implement Parcelable interface */
@@ -79,9 +86,11 @@
             new Parcelable.Creator<NativeWifiClient>() {
                 @Override
                 public NativeWifiClient createFromParcel(Parcel in) {
-                    byte[] macAddress = in.createByteArray();
-                    if (macAddress == null) {
-                        macAddress = new byte[0];
+                    MacAddress macAddress;
+                    try {
+                        macAddress = MacAddress.fromBytes(in.createByteArray());
+                    } catch (IllegalArgumentException e) {
+                        macAddress = null;
                     }
                     return new NativeWifiClient(macAddress);
                 }
diff --git a/wifi/java/android/net/wifi/wificond/WifiCondManager.java b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
index 7a31a5a..61f18e0 100644
--- a/wifi/java/android/net/wifi/wificond/WifiCondManager.java
+++ b/wifi/java/android/net/wifi/wificond/WifiCondManager.java
@@ -41,19 +41,16 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.stream.Collectors;
 
 /**
- * This class encapsulates the interface the wificond (Wi-Fi Conductor) daemon presents to the
- * Wi-Fi framework. The interface is only for use by the Wi-Fi framework and access is protected
- * by SELinux permissions: only the system server and wpa_supplicant can use WifiCondManager.
+ * This class encapsulates the interface the wificond daemon presents to the Wi-Fi framework. The
+ * interface is only for use by the Wi-Fi framework and access is protected by SELinux permissions.
  *
  * @hide
  */
@@ -371,7 +368,7 @@
         public void onConnectedClientsChanged(NativeWifiClient client, boolean isConnected) {
             if (mVerboseLoggingEnabled) {
                 Log.d(TAG, "onConnectedClientsChanged called with "
-                        + client.macAddress + " isConnected: " + isConnected);
+                        + client.getMacAddress() + " isConnected: " + isConnected);
             }
 
             Binder.clearCallingIdentity();
@@ -1046,13 +1043,13 @@
      * WifiScanner.WIFI_BAND_5_GHZ
      * WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY
      * WifiScanner.WIFI_BAND_6_GHZ
-     * @return frequencies List of valid frequencies (MHz), or an empty list for error.
+     * @return frequencies vector of valid frequencies (MHz), or an empty array for error.
      * @throws IllegalArgumentException if band is not recognized.
      */
-    public @NonNull List<Integer> getChannelsMhzForBand(@WifiAnnotations.WifiBandBasic int band) {
+    public @NonNull int[] getChannelsMhzForBand(@WifiAnnotations.WifiBandBasic int band) {
         if (mWificond == null) {
             Log.e(TAG, "No valid wificond scanner interface handler");
-            return Collections.emptyList();
+            return new int[0];
         }
         int[] result = null;
         try {
@@ -1076,9 +1073,9 @@
             Log.e(TAG, "Failed to request getChannelsForBand due to remote exception");
         }
         if (result == null) {
-            return Collections.emptyList();
+            result = new int[0];
         }
-        return Arrays.stream(result).boxed().collect(Collectors.toList());
+        return result;
     }
 
     /** Helper function to look up the interface handle using name */
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 53e9755..853212a 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1551,14 +1551,15 @@
      */
     @Test
     public void testGetAllMatchingWifiConfigs() throws Exception {
-        Map<String, List<ScanResult>> fqdns = new HashMap<>();
-        fqdns.put("www.test.com", new ArrayList<>());
-        when(mWifiService.getAllMatchingFqdnsForScanResults(any(List.class))).thenReturn(fqdns);
+        Map<String, List<ScanResult>> passpointProfiles = new HashMap<>();
+        passpointProfiles.put("www.test.com_987a69bca26", new ArrayList<>());
+        when(mWifiService.getAllMatchingPasspointProfilesForScanResults(
+                any(List.class))).thenReturn(passpointProfiles);
         InOrder inOrder = inOrder(mWifiService);
 
         mWifiManager.getAllMatchingWifiConfigs(new ArrayList<>());
 
-        inOrder.verify(mWifiService).getAllMatchingFqdnsForScanResults(any(List.class));
+        inOrder.verify(mWifiService).getAllMatchingPasspointProfilesForScanResults(any(List.class));
         inOrder.verify(mWifiService).getWifiConfigsForPasspointProfiles(any(List.class));
     }
 
@@ -2390,4 +2391,14 @@
         assertFalse(mWifiManager.isScanThrottleEnabled());
         verify(mWifiService).isScanThrottleEnabled();
     }
+
+    @Test
+    public void testAutoWakeup() throws Exception {
+        mWifiManager.setAutoWakeupEnabled(true);
+        verify(mWifiService).setAutoWakeupEnabled(true);
+
+        when(mWifiService.isAutoWakeupEnabled()).thenReturn(false);
+        assertFalse(mWifiManager.isAutoWakeupEnabled());
+        verify(mWifiService).isAutoWakeupEnabled();
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index ce542c2..8f6beb1 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -20,8 +20,11 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSp;
 import android.os.Parcel;
 
 import androidx.test.filters.SmallTest;
@@ -367,16 +370,55 @@
     }
 
     /**
-     * Verify that the unique identifier generated is correct.
+     * Verify that the unique identifier generated is identical for two instances
      *
      * @throws Exception
      */
     @Test
     public void validateUniqueId() throws Exception {
-        PasspointConfiguration config = PasspointTestUtils.createConfig();
-        String uniqueId;
-        uniqueId = config.getUniqueId();
-        assertEquals(uniqueId, config.getHomeSp().getFqdn());
+        PasspointConfiguration config1 = PasspointTestUtils.createConfig();
+        PasspointConfiguration config2 = PasspointTestUtils.createConfig();
+
+        assertEquals(config1.getUniqueId(), config2.getUniqueId());
+    }
+
+    /**
+     * Verify that the unique identifier generated is different for two instances with different
+     * HomeSp node
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateUniqueIdDifferentHomeSp() throws Exception {
+        PasspointConfiguration config1 = PasspointTestUtils.createConfig();
+
+        // Modify config2's RCOIs to a different set of values
+        PasspointConfiguration config2 = PasspointTestUtils.createConfig();
+        HomeSp homeSp = config2.getHomeSp();
+        homeSp.setRoamingConsortiumOis(new long[] {0xaa, 0xbb});
+        config2.setHomeSp(homeSp);
+
+        assertNotEquals(config1.getUniqueId(), config2.getUniqueId());
+    }
+
+    /**
+     * Verify that the unique identifier generated is different for two instances with different
+     * Credential node
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateUniqueIdDifferentCredential() throws Exception {
+        PasspointConfiguration config1 = PasspointTestUtils.createConfig();
+
+        // Modify config2's RCOIs to a different set of values
+        PasspointConfiguration config2 = PasspointTestUtils.createConfig();
+        Credential credential = config2.getCredential();
+        credential.setRealm("realm2.example.com");
+        credential.getSimCredential().setImsi("350460*");
+        config2.setCredential(credential);
+
+        assertNotEquals(config1.getUniqueId(), config2.getUniqueId());
     }
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
index 32105be..b745a34 100644
--- a/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/wificond/WifiCondManagerTest.java
@@ -38,6 +38,7 @@
 import android.app.AlarmManager;
 import android.app.test.TestAlarmManager;
 import android.content.Context;
+import android.net.MacAddress;
 import android.net.wifi.ScanResult;
 import android.net.wifi.SoftApInfo;
 import android.net.wifi.WifiConfiguration;
@@ -65,8 +66,6 @@
 import java.nio.charset.CharsetEncoder;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -121,7 +120,8 @@
     private static final String TEST_QUOTED_SSID_2 = "\"testSsid2\"";
     private static final int[] TEST_FREQUENCIES_1 = {};
     private static final int[] TEST_FREQUENCIES_2 = {2500, 5124};
-    private static final byte[] TEST_RAW_MAC_BYTES = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
+    private static final MacAddress TEST_RAW_MAC_BYTES = MacAddress.fromBytes(
+            new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05});
 
     private static final List<byte[]> SCAN_HIDDEN_NETWORK_SSID_LIST =
             new ArrayList<byte[]>() {{
@@ -742,43 +742,11 @@
         verify(deathHandler).run();
 
         // The handles should be cleared after death.
-        assertEquals(0, mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ).size());
+        assertEquals(0, mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ).length);
         verify(mWificond, never()).getAvailable5gNonDFSChannels();
     }
 
     /**
-     * Verify primitive array to list translation of channel API.
-     */
-    @Test
-    public void testGetChannels() throws Exception {
-        int[] resultsEmpty = new int[0];
-        int[] resultsSingle = new int[]{100};
-        int[] resultsMore = new int[]{100, 200};
-
-        List<Integer> emptyList = Collections.emptyList();
-        List<Integer> singleList = Arrays.asList(100);
-        List<Integer> moreList = Arrays.asList(100, 200);
-
-        when(mWificond.getAvailable2gChannels()).thenReturn(null);
-        assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ),
-                emptyList);
-        assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_5_GHZ),
-                emptyList);
-
-        when(mWificond.getAvailable2gChannels()).thenReturn(resultsEmpty);
-        assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ),
-                emptyList);
-
-        when(mWificond.getAvailable2gChannels()).thenReturn(resultsSingle);
-        assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ),
-                singleList);
-
-        when(mWificond.getAvailable2gChannels()).thenReturn(resultsMore);
-        assertEquals(mWificondControl.getChannelsMhzForBand(WifiScanner.WIFI_BAND_24_GHZ),
-                moreList);
-    }
-
-    /**
      * sendMgmtFrame() should fail if a null callback is passed in.
      */
     @Test