Merge "wifi: add interface name to ScanResult"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 2fd2e33..fc5efc6 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -6,6 +6,7 @@
 clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
                cmds/hid/
                cmds/input/
+               cmds/uinput/
                core/jni/
                libs/input/
                services/core/jni/
diff --git a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
index 050fecd..d3938f4 100644
--- a/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/ResourcesManagerPerfTest.java
@@ -17,7 +17,6 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.view.Display;
@@ -136,4 +135,22 @@
             }
         }
     }
+
+    @Test
+    public void getDisplayMetrics() {
+        ResourcesManager resourcesManager = ResourcesManager.getInstance();
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            state.pauseTiming();
+            // Invalidate cache.
+            resourcesManager.applyConfigurationToResourcesLocked(
+                    resourcesManager.getConfiguration(), null);
+            state.resumeTiming();
+
+            // Invoke twice for testing cache.
+            resourcesManager.getDisplayMetrics();
+            resourcesManager.getDisplayMetrics();
+        }
+    }
 }
diff --git a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
index 93bf541..e192861 100644
--- a/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
+++ b/apct-tests/perftests/utils/src/android/perftests/utils/BenchmarkState.java
@@ -53,7 +53,8 @@
     private static final int NOT_STARTED = 0;  // The benchmark has not started yet.
     private static final int WARMUP = 1; // The benchmark is warming up.
     private static final int RUNNING = 2;  // The benchmark is running.
-    private static final int FINISHED = 3;  // The benchmark has stopped.
+    private static final int RUNNING_CUSTOMIZED = 3;  // Running for customized measurement.
+    private static final int FINISHED = 4;  // The benchmark has stopped.
 
     private int mState = NOT_STARTED;  // Current benchmark state.
 
@@ -76,6 +77,14 @@
 
     private int mRepeatCount = 0;
 
+    /**
+     * Additional iteration that used to apply customized measurement. The result during these
+     * iterations won't be counted into {@link #mStats}.
+     */
+    private int mMaxCustomizedIterations;
+    private int mCustomizedIterations;
+    private CustomizedIterationListener mCustomizedIterationListener;
+
     // Statistics. These values will be filled when the benchmark has finished.
     // The computation needs double precision, but long int is fine for final reporting.
     private Stats mStats;
@@ -110,6 +119,15 @@
         mPaused = false;
     }
 
+    /**
+     * This is used to run the benchmark with more information by enabling some debug mechanism but
+     * we don't want to account the special runs (slower) in the stats report.
+     */
+    public void setCustomizedIterations(int iterations, CustomizedIterationListener listener) {
+        mMaxCustomizedIterations = iterations;
+        mCustomizedIterationListener = listener;
+    }
+
     private void beginWarmup() {
         mStartTimeNs = System.nanoTime();
         mIteration = 0;
@@ -141,6 +159,11 @@
                 Debug.stopMethodTracing();
             }
             mStats = new Stats(mResults);
+            if (mMaxCustomizedIterations > 0 && mCustomizedIterationListener != null) {
+                mState = RUNNING_CUSTOMIZED;
+                mCustomizedIterationListener.onStart(mCustomizedIterations);
+                return true;
+            }
             mState = FINISHED;
             return false;
         }
@@ -180,6 +203,15 @@
                             "Resume the benchmark before finishing each step.");
                 }
                 return true;
+            case RUNNING_CUSTOMIZED:
+                mCustomizedIterationListener.onFinished(mCustomizedIterations);
+                mCustomizedIterations++;
+                if (mCustomizedIterations >= mMaxCustomizedIterations) {
+                    mState = FINISHED;
+                    return false;
+                }
+                mCustomizedIterationListener.onStart(mCustomizedIterations);
+                return true;
             case FINISHED:
                 throw new IllegalStateException("The benchmark has finished.");
             default:
@@ -240,4 +272,13 @@
         status.putLong(key + "_standardDeviation", standardDeviation());
         instrumentation.sendStatus(Activity.RESULT_OK, status);
     }
+
+    /** The interface to receive the events of customized iteration. */
+    public interface CustomizedIterationListener {
+        /** The customized iteration starts. */
+        void onStart(int iteration);
+
+        /** The customized iteration finished. */
+        void onFinished(int iteration);
+    }
 }
diff --git a/apct-tests/perftests/windowmanager/AndroidManifest.xml b/apct-tests/perftests/windowmanager/AndroidManifest.xml
index 85fd7176c..95ede34 100644
--- a/apct-tests/perftests/windowmanager/AndroidManifest.xml
+++ b/apct-tests/perftests/windowmanager/AndroidManifest.xml
@@ -29,5 +29,7 @@
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-        android:targetPackage="com.android.perftests.wm"/>
+        android:targetPackage="com.android.perftests.wm">
+        <meta-data android:name="listener" android:value="android.wm.WmPerfRunListener" />
+    </instrumentation>
 </manifest>
diff --git a/apct-tests/perftests/windowmanager/AndroidTest.xml b/apct-tests/perftests/windowmanager/AndroidTest.xml
index aee02c7a..6ac9f93 100644
--- a/apct-tests/perftests/windowmanager/AndroidTest.xml
+++ b/apct-tests/perftests/windowmanager/AndroidTest.xml
@@ -42,7 +42,7 @@
         <option name="hidden-api-checks" value="false"/>
 
         <!-- Listener related args for collecting the traces and waiting for the device to stabilize. -->
-        <option name="device-listeners" value="android.wm.WmPerfRunListener,android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
+        <option name="device-listeners" value="android.device.collectors.ProcLoadListener,android.device.collectors.PerfettoListener" />
 
         <!-- Guarantee that user defined RunListeners will be running before any of the default listeners defined in this runner. -->
         <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
@@ -57,8 +57,6 @@
         <!-- PerfettoListener related arguments -->
         <option name="instrumentation-arg" key="perfetto_config_text_proto" value="true" />
         <option name="instrumentation-arg" key="perfetto_config_file" value="trace_config.textproto" />
-
-        <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
     </test>
 
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
diff --git a/apct-tests/perftests/windowmanager/README.md b/apct-tests/perftests/windowmanager/README.md
index 05fa627..8b5292f 100644
--- a/apct-tests/perftests/windowmanager/README.md
+++ b/apct-tests/perftests/windowmanager/README.md
@@ -25,3 +25,11 @@
           com.android.perftests.wm/androidx.test.runner.AndroidJUnitRunner
 ```
 * `kill-bg` is optional.
+
+Test arguments
+ - kill-bg
+   * boolean: Kill background process before running test.
+ - profiling-iterations
+   * int: Run the extra iterations with enabling method profiling.
+ - profiling-sampling
+   * int: The interval (0=trace each method, default is 10) of sample profiling in microseconds.
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java
index 4ed3b4e..5c09ec2 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/InternalWindowOperationPerfTest.java
@@ -41,7 +41,8 @@
 
 /** Measure the performance of internal methods in window manager service by trace tag. */
 @LargeTest
-public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase {
+public class InternalWindowOperationPerfTest extends WindowManagerPerfTestBase
+        implements ManualBenchmarkState.CustomizedIterationListener {
     private static final String TAG = InternalWindowOperationPerfTest.class.getSimpleName();
 
     @Rule
@@ -68,6 +69,9 @@
             "finishActivity",
             "startActivityInner");
 
+    private boolean mIsProfiling;
+    private boolean mIsTraceStarted;
+
     @Test
     @ManualBenchmarkTest(
             targetTestDurationNs = 20 * TIME_1_S_IN_NS,
@@ -76,13 +80,13 @@
                             | StatsReport.FLAG_MAX | StatsReport.FLAG_COEFFICIENT_VAR))
     public void testLaunchAndFinishActivity() throws Throwable {
         final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        state.setCustomizedIterations(getProfilingIterations(), this);
         long measuredTimeNs = 0;
-        boolean isTraceStarted = false;
 
         while (state.keepRunning(measuredTimeNs)) {
-            if (!isTraceStarted && !state.isWarmingUp()) {
+            if (!mIsTraceStarted && !mIsProfiling && !state.isWarmingUp()) {
                 startAsyncAtrace();
-                isTraceStarted = true;
+                mIsTraceStarted = true;
             }
             final long startTime = SystemClock.elapsedRealtimeNanos();
             mActivityRule.launchActivity();
@@ -91,7 +95,9 @@
             measuredTimeNs = SystemClock.elapsedRealtimeNanos() - startTime;
         }
 
-        stopAsyncAtrace();
+        if (mIsTraceStarted) {
+            stopAsyncAtrace();
+        }
 
         mTraceMarkParser.forAllSlices((key, slices) -> {
             for (TraceMarkSlice slice : slices) {
@@ -108,7 +114,7 @@
         SystemClock.sleep(TimeUnit.NANOSECONDS.toMillis(TIME_1_S_IN_NS));
     }
 
-    private void stopAsyncAtrace() throws IOException {
+    private void stopAsyncAtrace() {
         final ParcelFileDescriptor pfd = sUiAutomation.executeShellCommand("atrace --async_stop");
         final InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
         try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
@@ -116,6 +122,28 @@
             while ((line = reader.readLine()) != null) {
                 mTraceMarkParser.visit(line);
             }
+        } catch (IOException e) {
+            Log.w(TAG, "Failed to read the result of stopped atrace", e);
+        }
+    }
+
+    @Override
+    public void onStart(int iteration) {
+        if (mIsTraceStarted) {
+            // Do not capture trace when profiling because the result will be much slower.
+            stopAsyncAtrace();
+            mIsTraceStarted = false;
+        }
+        mIsProfiling = true;
+        startProfiling(InternalWindowOperationPerfTest.class.getSimpleName()
+                + "_MethodTracing_" + iteration + ".trace");
+    }
+
+    @Override
+    public void onFinished(int iteration) {
+        stopProfiling();
+        if (iteration >= getProfilingIterations() - 1) {
+            mIsProfiling = false;
         }
     }
 }
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
index 6122ef2..efcabd8 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RecentsAnimationPerfTest.java
@@ -62,7 +62,8 @@
 
 @RunWith(Parameterized.class)
 @LargeTest
-public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase {
+public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase
+        implements ManualBenchmarkState.CustomizedIterationListener {
     private static Intent sRecentsIntent;
 
     @Rule
@@ -162,6 +163,7 @@
                     | StatsReport.FLAG_COEFFICIENT_VAR))
     public void testRecentsAnimation() throws Throwable {
         final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        state.setCustomizedIterations(getProfilingIterations(), this);
         final IActivityTaskManager atm = ActivityTaskManager.getService();
 
         final ArrayList<Pair<String, Boolean>> finishCases = new ArrayList<>();
@@ -230,7 +232,21 @@
             state.addExtraResult("start", elapsedTimeNsOfStart);
 
             // Ensure the animation callback is done.
-            Assume.assumeTrue(recentsSemaphore.tryAcquire(TIME_5_S_IN_NS, TimeUnit.NANOSECONDS));
+            Assume.assumeTrue(recentsSemaphore.tryAcquire(
+                    sIsProfilingMethod ? 10 * TIME_5_S_IN_NS : TIME_5_S_IN_NS,
+                    TimeUnit.NANOSECONDS));
         }
     }
+
+    @Override
+    public void onStart(int iteration) {
+        startProfiling(RecentsAnimationPerfTest.class.getSimpleName()
+                + "_interval_" + intervalBetweenOperations
+                + "_MethodTracing_" + iteration + ".trace");
+    }
+
+    @Override
+    public void onFinished(int iteration) {
+        stopProfiling();
+    }
 }
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
index cff5663..2697428 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java
@@ -54,7 +54,8 @@
 @RunWith(Parameterized.class)
 @LargeTest
 @Presubmit
-public class RelayoutPerfTest extends WindowManagerPerfTestBase {
+public class RelayoutPerfTest extends WindowManagerPerfTestBase
+        implements BenchmarkState.CustomizedIterationListener {
     private int mIteration;
 
     @Rule
@@ -93,9 +94,22 @@
         mActivityRule.runOnUiThread(() -> activity.setContentView(contentView));
         getInstrumentation().waitForIdleSync();
 
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        state.setCustomizedIterations(getProfilingIterations(), this);
         final RelayoutRunner relayoutRunner = new RelayoutRunner(activity, contentView.getWindow(),
                 () -> visibilities[mIteration++ % visibilities.length]);
-        relayoutRunner.runBenchmark(mPerfStatusReporter.getBenchmarkState());
+        relayoutRunner.runBenchmark(state);
+    }
+
+    @Override
+    public void onStart(int iteration) {
+        startProfiling(RelayoutPerfTest.class.getSimpleName() + "_" + testName
+                + "_MethodTracing_" + iteration + ".trace");
+    }
+
+    @Override
+    public void onFinished(int iteration) {
+        stopProfiling();
     }
 
     /** A dummy view to get IWindow. */
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index c72cc9d..c52b130 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -48,8 +48,6 @@
 public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase
         implements ManualBenchmarkState.CustomizedIterationListener {
 
-    private static final int PROFILED_ITERATIONS = 2;
-
     @Rule
     public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
 
@@ -64,7 +62,7 @@
         sUiAutomation.dropShellPermissionIdentity();
     }
 
-    /** The last {@link #PROFILED_ITERATIONS} will provide the information of method profiling. */
+    /** The last customized iterations will provide the information of method profiling. */
     @Override
     public void onStart(int iteration) {
         startProfiling(WindowAddRemovePerfTest.class.getSimpleName()
@@ -80,7 +78,7 @@
     @ManualBenchmarkTest(warmupDurationNs = TIME_1_S_IN_NS, targetTestDurationNs = TIME_5_S_IN_NS)
     public void testAddRemoveWindow() throws Throwable {
         final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        state.setCustomizedIterations(PROFILED_ITERATIONS, this);
+        state.setCustomizedIterations(getProfilingIterations(), this);
         new TestWindow().runBenchmark(state);
     }
 
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
index cc0e939a..b51a9a8 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowManagerPerfTestBase.java
@@ -32,6 +32,7 @@
 import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry;
 import androidx.test.runner.lifecycle.Stage;
 
+import org.junit.After;
 import org.junit.BeforeClass;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
@@ -52,18 +53,17 @@
 
     /**
      * The out directory matching the directory-keys of collector in AndroidTest.xml. The directory
-     * is in /data because while enabling method profling of system server, it cannot write the
+     * is in /data because while enabling method profiling of system server, it cannot write the
      * trace to external storage.
      */
     static final File BASE_OUT_PATH = new File("/data/local/tmp/WmPerfTests");
 
+    static boolean sIsProfilingMethod;
+
     @BeforeClass
     public static void setUpOnce() {
         final Context context = getInstrumentation().getContext();
 
-        if (!BASE_OUT_PATH.exists()) {
-            executeShellCommand("mkdir -p " + BASE_OUT_PATH);
-        }
         if (!context.getSystemService(PowerManager.class).isInteractive()
                 || context.getSystemService(KeyguardManager.class).isKeyguardLocked()) {
             executeShellCommand("input keyevent KEYCODE_WAKEUP");
@@ -73,6 +73,14 @@
                 .addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
     }
 
+    @After
+    public void tearDown() {
+        // Make sure that profiling is stopped if test fails.
+        if (sIsProfilingMethod) {
+            stopProfiling();
+        }
+    }
+
     /**
      * Executes shell command with reading the output. It may also used to block until the current
      * command is completed.
@@ -93,12 +101,26 @@
     }
 
     /** Starts method tracing on system server. */
-    void startProfiling(String subPath) {
-        executeShellCommand("am profile start system " + new File(BASE_OUT_PATH, subPath));
+    static void startProfiling(String subPath) {
+        if (!BASE_OUT_PATH.exists()) {
+            executeShellCommand("mkdir -p " + BASE_OUT_PATH);
+        }
+        final String samplingArg = WmPerfRunListener.sSamplingIntervalUs > 0
+                ? ("--sampling " + WmPerfRunListener.sSamplingIntervalUs)
+                : "";
+        executeShellCommand("am profile start " + samplingArg + " system "
+                + new File(BASE_OUT_PATH, subPath));
+        sIsProfilingMethod = true;
     }
 
-    void stopProfiling() {
+    static void stopProfiling() {
         executeShellCommand("am profile stop system");
+        sIsProfilingMethod = false;
+    }
+
+    /** Returns how many iterations should run with method tracing. */
+    static int getProfilingIterations() {
+        return WmPerfRunListener.sProfilingIterations;
     }
 
     static void runWithShellPermissionIdentity(Runnable runnable) {
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
index 6eb85aa..d955289 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WmPerfRunListener.java
@@ -31,6 +31,7 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.provider.Settings;
+import android.util.Log;
 import android.view.WindowManagerPolicyConstants;
 import android.wm.WindowManagerPerfTestBase.SettingsSession;
 
@@ -46,10 +47,22 @@
 
 /** Prepare the preconditions before running performance test. */
 public class WmPerfRunListener extends RunListener {
+    private static final String TAG = WmPerfRunListener.class.getSimpleName();
 
-    private static final String OPTION_KILL_BACKGROUND = "kill-bg";
+    private static final String ARGUMENT_LOG_ONLY = "log";
+    private static final String ARGUMENT_KILL_BACKGROUND = "kill-bg";
+    private static final String ARGUMENT_PROFILING_ITERATIONS = "profiling-iterations";
+    private static final String ARGUMENT_PROFILING_SAMPLING = "profiling-sampling";
+    private static final String DEFAULT_PROFILING_ITERATIONS = "0";
+    private static final String DEFAULT_PROFILING_SAMPLING_US = "10";
     private static final long KILL_BACKGROUND_WAIT_MS = 3000;
 
+    /** The requested iterations to run with method profiling. */
+    static int sProfilingIterations;
+
+    /** The interval of sample profiling in microseconds. */
+    static int sSamplingIntervalUs;
+
     private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
     private long mWaitPreconditionDoneMs = 500;
 
@@ -83,6 +96,16 @@
     @Override
     public void testRunStarted(Description description) {
         final Bundle arguments = InstrumentationRegistry.getArguments();
+        // If true, it only logs the method names without running.
+        final boolean skip = Boolean.parseBoolean(arguments.getString(ARGUMENT_LOG_ONLY, "false"));
+        Log.i(TAG, "arguments=" + arguments);
+        if (skip) {
+            return;
+        }
+        sProfilingIterations = Integer.parseInt(
+                arguments.getString(ARGUMENT_PROFILING_ITERATIONS, DEFAULT_PROFILING_ITERATIONS));
+        sSamplingIntervalUs = Integer.parseInt(
+                arguments.getString(ARGUMENT_PROFILING_SAMPLING, DEFAULT_PROFILING_SAMPLING_US));
 
         // Use gesture navigation for consistency.
         mNavigationModeSetting.set(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
@@ -97,7 +120,7 @@
         });
         PhoneWindow.sendCloseSystemWindows(mContext, "WmPerfTests");
 
-        if (Boolean.parseBoolean(arguments.getString(OPTION_KILL_BACKGROUND))) {
+        if (Boolean.parseBoolean(arguments.getString(ARGUMENT_KILL_BACKGROUND))) {
             runWithShellPermissionIdentity(this::killBackgroundProcesses);
             mWaitPreconditionDoneMs = KILL_BACKGROUND_WAIT_MS;
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/JobSchedulerBackgroundThread.java b/apex/jobscheduler/service/java/com/android/server/JobSchedulerBackgroundThread.java
index 91d254d..a413f7b 100644
--- a/apex/jobscheduler/service/java/com/android/server/JobSchedulerBackgroundThread.java
+++ b/apex/jobscheduler/service/java/com/android/server/JobSchedulerBackgroundThread.java
@@ -17,10 +17,13 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.HandlerExecutor;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Trace;
 
+import java.util.concurrent.Executor;
+
 /**
  * Shared singleton background thread.
  *
@@ -31,6 +34,7 @@
     private static final long SLOW_DELIVERY_THRESHOLD_MS = 30_000;
     private static JobSchedulerBackgroundThread sInstance;
     private static Handler sHandler;
+    private static Executor sHandlerExecutor;
 
     private JobSchedulerBackgroundThread() {
         super("jobscheduler.bg", android.os.Process.THREAD_PRIORITY_BACKGROUND);
@@ -45,6 +49,7 @@
             looper.setSlowLogThresholdMs(
                     SLOW_DISPATCH_THRESHOLD_MS, SLOW_DELIVERY_THRESHOLD_MS);
             sHandler = new Handler(sInstance.getLooper());
+            sHandlerExecutor = new HandlerExecutor(sHandler);
         }
     }
 
@@ -63,4 +68,12 @@
             return sHandler;
         }
     }
+
+    /** Returns the singleton handler executor for JobSchedulerBackgroundThread */
+    public static Executor getExecutor() {
+        synchronized (JobSchedulerBackgroundThread.class) {
+            ensureThreadLocked();
+            return sHandlerExecutor;
+        }
+    }
 }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index a1a5004..b35a7be 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -166,7 +166,7 @@
                 // Note: we can't directly do postDelayed(this::rampUpForScreenOn), because
                 // we need the exact same instance for removeCallbacks().
                 mHandler.postDelayed(mRampUpForScreenOff,
-                        mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.getValue());
+                        mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
             }
         }
     }
@@ -189,7 +189,7 @@
             }
             final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
             if ((mLastScreenOffRealtime
-                    + mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.getValue())
+                    + mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS)
                     > now) {
                 return;
             }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 3234b27..cf4caea 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -39,7 +39,6 @@
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -50,7 +49,6 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ServiceInfo;
-import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
@@ -67,11 +65,10 @@
 import android.os.UserHandle;
 import android.os.UserManagerInternal;
 import android.os.WorkSource;
-import android.provider.Settings;
+import android.provider.DeviceConfig;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.IndentingPrintWriter;
-import android.util.KeyValueListParser;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -88,6 +85,7 @@
 import com.android.server.AppStateTracker;
 import com.android.server.AppStateTrackerImpl;
 import com.android.server.DeviceIdleInternal;
+import com.android.server.JobSchedulerBackgroundThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService.TargetUser;
 import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
@@ -329,39 +327,70 @@
 
     // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
 
-    private class ConstantsObserver extends ContentObserver {
-        private ContentResolver mResolver;
-
-        public ConstantsObserver(Handler handler) {
-            super(handler);
-        }
-
-        public void start(ContentResolver resolver) {
-            mResolver = resolver;
-            mResolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
-            updateConstants();
+    private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener {
+        public void start() {
+            DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                    JobSchedulerBackgroundThread.getExecutor(), this);
+            // Load all the constants.
+            onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
         }
 
         @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            updateConstants();
-        }
-
-        private void updateConstants() {
+        public void onPropertiesChanged(DeviceConfig.Properties properties) {
+            boolean apiQuotaScheduleUpdated = false;
+            boolean concurrencyUpdated = false;
             synchronized (mLock) {
-                try {
-                    mConstants.updateConstantsLocked(Settings.Global.getString(mResolver,
-                            Settings.Global.JOB_SCHEDULER_CONSTANTS));
-                    for (int controller = 0; controller < mControllers.size(); controller++) {
-                        final StateController sc = mControllers.get(controller);
-                        sc.onConstantsUpdatedLocked();
+                for (String name : properties.getKeyset()) {
+                    if (name == null) {
+                        continue;
                     }
-                    updateQuotaTracker();
-                } catch (IllegalArgumentException e) {
-                    // Failed to parse the settings string, log this and move on
-                    // with defaults.
-                    Slog.e(TAG, "Bad jobscheduler settings", e);
+                    switch (name) {
+                        case Constants.KEY_ENABLE_API_QUOTAS:
+                        case Constants.KEY_API_QUOTA_SCHEDULE_COUNT:
+                        case Constants.KEY_API_QUOTA_SCHEDULE_WINDOW_MS:
+                        case Constants.KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT:
+                        case Constants.KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION:
+                            if (!apiQuotaScheduleUpdated) {
+                                mConstants.updateApiQuotaConstantsLocked();
+                                updateQuotaTracker();
+                                apiQuotaScheduleUpdated = true;
+                            }
+                            break;
+                        case Constants.KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT:
+                        case Constants.KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS:
+                            mConstants.updateBatchingConstantsLocked();
+                            break;
+                        case Constants.KEY_HEAVY_USE_FACTOR:
+                        case Constants.KEY_MODERATE_USE_FACTOR:
+                            mConstants.updateUseFactorConstantsLocked();
+                            break;
+                        case Constants.KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS:
+                            if (!concurrencyUpdated) {
+                                mConstants.updateConcurrencyConstantsLocked();
+                                concurrencyUpdated = true;
+                            }
+                            break;
+                        case Constants.KEY_MIN_LINEAR_BACKOFF_TIME_MS:
+                        case Constants.KEY_MIN_EXP_BACKOFF_TIME_MS:
+                            mConstants.updateBackoffConstantsLocked();
+                            break;
+                        case Constants.KEY_CONN_CONGESTION_DELAY_FRAC:
+                        case Constants.KEY_CONN_PREFETCH_RELAX_FRAC:
+                            mConstants.updateConnectivityConstantsLocked();
+                            break;
+                        default:
+                            // Too many max_job_* strings to list.
+                            if (name.startsWith(Constants.KEY_PREFIX_MAX_JOB)
+                                    && !concurrencyUpdated) {
+                                mConstants.updateConcurrencyConstantsLocked();
+                                concurrencyUpdated = true;
+                            }
+                            break;
+                    }
+                }
+                for (int controller = 0; controller < mControllers.size(); controller++) {
+                    final StateController sc = mControllers.get(controller);
+                    sc.onConstantsUpdatedLocked();
                 }
             }
         }
@@ -376,53 +405,52 @@
     }
 
     static class MaxJobCounts {
-        private final KeyValueListParser.IntValue mTotal;
-        private final KeyValueListParser.IntValue mMaxBg;
-        private final KeyValueListParser.IntValue mMinBg;
+        private final int mTotalDefault;
+        private final String mTotalKey;
+        private final int mMaxBgDefault;
+        private final String mMaxBgKey;
+        private final int mMinBgDefault;
+        private final String mMinBgKey;
+        private int mTotal;
+        private int mMaxBg;
+        private int mMinBg;
 
         MaxJobCounts(int totalDefault, String totalKey,
                 int maxBgDefault, String maxBgKey, int minBgDefault, String minBgKey) {
-            mTotal = new KeyValueListParser.IntValue(totalKey, totalDefault);
-            mMaxBg = new KeyValueListParser.IntValue(maxBgKey, maxBgDefault);
-            mMinBg = new KeyValueListParser.IntValue(minBgKey, minBgDefault);
+            mTotalKey = totalKey;
+            mTotal = mTotalDefault = totalDefault;
+            mMaxBgKey = maxBgKey;
+            mMaxBg = mMaxBgDefault = maxBgDefault;
+            mMinBgKey = minBgKey;
+            mMinBg = mMinBgDefault = minBgDefault;
         }
 
-        public void parse(KeyValueListParser parser) {
-            mTotal.parse(parser);
-            mMaxBg.parse(parser);
-            mMinBg.parse(parser);
+        public void update() {
+            mTotal = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                    mTotalKey, mTotalDefault);
+            mMaxBg = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                    mMaxBgKey, mMaxBgDefault);
+            mMinBg = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                    mMinBgKey, mMinBgDefault);
 
-            if (mTotal.getValue() < 1) {
-                mTotal.setValue(1);
-            } else if (mTotal.getValue() > MAX_JOB_CONTEXTS_COUNT) {
-                mTotal.setValue(MAX_JOB_CONTEXTS_COUNT);
-            }
+            // Ensure total in the range [1, MAX_JOB_CONTEXTS_COUNT].
+            mTotal = Math.min(Math.max(1, mTotal), MAX_JOB_CONTEXTS_COUNT);
 
-            if (mMaxBg.getValue() < 1) {
-                mMaxBg.setValue(1);
-            } else if (mMaxBg.getValue() > mTotal.getValue()) {
-                mMaxBg.setValue(mTotal.getValue());
-            }
-            if (mMinBg.getValue() < 0) {
-                mMinBg.setValue(0);
-            } else {
-                if (mMinBg.getValue() > mMaxBg.getValue()) {
-                    mMinBg.setValue(mMaxBg.getValue());
-                }
-                if (mMinBg.getValue() >= mTotal.getValue()) {
-                    mMinBg.setValue(mTotal.getValue() - 1);
-                }
-            }
+            // Ensure maxBg in the range [1, total].
+            mMaxBg = Math.min(Math.max(1, mMaxBg), mTotal);
+
+            // Ensure minBg in the range [0, min(maxBg, total - 1)]
+            mMinBg = Math.min(Math.max(0, mMinBg), Math.min(mMaxBg, mTotal - 1));
         }
 
         /** Total number of jobs to run simultaneously. */
         public int getMaxTotal() {
-            return mTotal.getValue();
+            return mTotal;
         }
 
         /** Max number of BG (== owned by non-TOP apps) jobs to run simultaneously. */
         public int getMaxBg() {
-            return mMaxBg.getValue();
+            return mMaxBg;
         }
 
         /**
@@ -430,20 +458,34 @@
          * pending, rather than always running the TOTAL number of FG jobs.
          */
         public int getMinBg() {
-            return mMinBg.getValue();
+            return mMinBg;
         }
 
         public void dump(PrintWriter pw, String prefix) {
-            mTotal.dump(pw, prefix);
-            mMaxBg.dump(pw, prefix);
-            mMinBg.dump(pw, prefix);
+            pw.print(prefix);
+            pw.print(mTotalKey);
+            pw.print("=");
+            pw.print(mTotal);
+            pw.println();
+
+            pw.print(prefix);
+            pw.print(mMaxBgKey);
+            pw.print("=");
+            pw.print(mMaxBg);
+            pw.println();
+
+            pw.print(prefix);
+            pw.print(mMinBgKey);
+            pw.print("=");
+            pw.print(mMinBg);
+            pw.println();
         }
 
         public void dumpProto(ProtoOutputStream proto, long fieldId) {
             final long token = proto.start(fieldId);
-            mTotal.dumpProto(proto, MaxJobCountsProto.TOTAL_JOBS);
-            mMaxBg.dumpProto(proto, MaxJobCountsProto.MAX_BG);
-            mMinBg.dumpProto(proto, MaxJobCountsProto.MIN_BG);
+            proto.write(MaxJobCountsProto.TOTAL_JOBS, mTotal);
+            proto.write(MaxJobCountsProto.MAX_BG, mMaxBg);
+            proto.write(MaxJobCountsProto.MIN_BG, mMinBg);
             proto.end(token);
         }
     }
@@ -476,23 +518,11 @@
     }
 
     /**
-     * All times are in milliseconds. These constants are kept synchronized with the system
-     * global Settings. Any access to this class or its fields should be done while
+     * All times are in milliseconds. Any access to this class or its fields should be done while
      * holding the JobSchedulerService.mLock lock.
      */
     public static class Constants {
         // Key names stored in the settings value.
-        // TODO(124466289): remove deprecated flags when we migrate to DeviceConfig
-        private static final String DEPRECATED_KEY_MIN_IDLE_COUNT = "min_idle_count";
-        private static final String DEPRECATED_KEY_MIN_CHARGING_COUNT = "min_charging_count";
-        private static final String DEPRECATED_KEY_MIN_BATTERY_NOT_LOW_COUNT =
-                "min_battery_not_low_count";
-        private static final String DEPRECATED_KEY_MIN_STORAGE_NOT_LOW_COUNT =
-                "min_storage_not_low_count";
-        private static final String DEPRECATED_KEY_MIN_CONNECTIVITY_COUNT =
-                "min_connectivity_count";
-        private static final String DEPRECATED_KEY_MIN_CONTENT_COUNT = "min_content_count";
-        private static final String DEPRECATED_KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
         private static final String KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT =
                 "min_ready_non_active_jobs_count";
         private static final String KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS =
@@ -500,28 +530,10 @@
         private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
         private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
 
-        // The following values used to be used on P and below. Do not reuse them.
-        private static final String DEPRECATED_KEY_FG_JOB_COUNT = "fg_job_count";
-        private static final String DEPRECATED_KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
-        private static final String DEPRECATED_KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
-        private static final String DEPRECATED_KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
-        private static final String DEPRECATED_KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
-
-        private static final String DEPRECATED_KEY_MAX_STANDARD_RESCHEDULE_COUNT
-                = "max_standard_reschedule_count";
-        private static final String DEPRECATED_KEY_MAX_WORK_RESCHEDULE_COUNT =
-                "max_work_reschedule_count";
-        private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
-        private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
-        private static final String DEPRECATED_KEY_STANDBY_HEARTBEAT_TIME =
-                "standby_heartbeat_time";
-        private static final String DEPRECATED_KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
-        private static final String DEPRECATED_KEY_STANDBY_FREQUENT_BEATS =
-                "standby_frequent_beats";
-        private static final String DEPRECATED_KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
+        private static final String KEY_MIN_LINEAR_BACKOFF_TIME_MS = "min_linear_backoff_time_ms";
+        private static final String KEY_MIN_EXP_BACKOFF_TIME_MS = "min_exp_backoff_time_ms";
         private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
         private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
-        private static final String DEPRECATED_KEY_USE_HEARTBEATS = "use_heartbeats";
         private static final String KEY_ENABLE_API_QUOTAS = "enable_api_quotas";
         private static final String KEY_API_QUOTA_SCHEDULE_COUNT = "aq_schedule_count";
         private static final String KEY_API_QUOTA_SCHEDULE_WINDOW_MS = "aq_schedule_window_ms";
@@ -530,12 +542,15 @@
         private static final String KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT =
                 "aq_schedule_return_failure";
 
+        private static final String KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS =
+                "screen_off_job_concurrency_increase_delay_ms";
+
         private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
         private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS;
         private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
         private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
-        private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
-        private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
+        private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS;
+        private static final long DEFAULT_MIN_EXP_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS;
         private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
         private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
         private static final boolean DEFAULT_ENABLE_API_QUOTAS = true;
@@ -543,6 +558,7 @@
         private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS;
         private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true;
         private static final boolean DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false;
+        private static final long DEFAULT_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS = 30_000;
 
         /**
          * Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
@@ -564,59 +580,61 @@
          */
         float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
 
+        /** Prefix for all of the max_job constants. */
+        private static final String KEY_PREFIX_MAX_JOB = "max_job_";
+
         // Max job counts for screen on / off, for each memory trim level.
         final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_ON =
                 new MaxJobCountsPerMemoryTrimLevel(
                         new MaxJobCounts(
-                                8, "max_job_total_on_normal",
-                                6, "max_job_max_bg_on_normal",
-                                2, "max_job_min_bg_on_normal"),
+                                8, KEY_PREFIX_MAX_JOB + "total_on_normal",
+                                6, KEY_PREFIX_MAX_JOB + "max_bg_on_normal",
+                                2, KEY_PREFIX_MAX_JOB + "min_bg_on_normal"),
                         new MaxJobCounts(
-                                8, "max_job_total_on_moderate",
-                                4, "max_job_max_bg_on_moderate",
-                                2, "max_job_min_bg_on_moderate"),
+                                8, KEY_PREFIX_MAX_JOB + "total_on_moderate",
+                                4, KEY_PREFIX_MAX_JOB + "max_bg_on_moderate",
+                                2, KEY_PREFIX_MAX_JOB + "min_bg_on_moderate"),
                         new MaxJobCounts(
-                                5, "max_job_total_on_low",
-                                1, "max_job_max_bg_on_low",
-                                1, "max_job_min_bg_on_low"),
+                                5, KEY_PREFIX_MAX_JOB + "total_on_low",
+                                1, KEY_PREFIX_MAX_JOB + "max_bg_on_low",
+                                1, KEY_PREFIX_MAX_JOB + "min_bg_on_low"),
                         new MaxJobCounts(
-                                5, "max_job_total_on_critical",
-                                1, "max_job_max_bg_on_critical",
-                                1, "max_job_min_bg_on_critical"));
+                                5, KEY_PREFIX_MAX_JOB + "total_on_critical",
+                                1, KEY_PREFIX_MAX_JOB + "max_bg_on_critical",
+                                1, KEY_PREFIX_MAX_JOB + "min_bg_on_critical"));
 
         final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_OFF =
                 new MaxJobCountsPerMemoryTrimLevel(
                         new MaxJobCounts(
-                                10, "max_job_total_off_normal",
-                                6, "max_job_max_bg_off_normal",
-                                2, "max_job_min_bg_off_normal"),
+                                10, KEY_PREFIX_MAX_JOB + "total_off_normal",
+                                6, KEY_PREFIX_MAX_JOB + "max_bg_off_normal",
+                                2, KEY_PREFIX_MAX_JOB + "min_bg_off_normal"),
                         new MaxJobCounts(
-                                10, "max_job_total_off_moderate",
-                                4, "max_job_max_bg_off_moderate",
-                                2, "max_job_min_bg_off_moderate"),
+                                10, KEY_PREFIX_MAX_JOB + "total_off_moderate",
+                                4, KEY_PREFIX_MAX_JOB + "max_bg_off_moderate",
+                                2, KEY_PREFIX_MAX_JOB + "min_bg_off_moderate"),
                         new MaxJobCounts(
-                                5, "max_job_total_off_low",
-                                1, "max_job_max_bg_off_low",
-                                1, "max_job_min_bg_off_low"),
+                                5, KEY_PREFIX_MAX_JOB + "total_off_low",
+                                1, KEY_PREFIX_MAX_JOB + "max_bg_off_low",
+                                1, KEY_PREFIX_MAX_JOB + "min_bg_off_low"),
                         new MaxJobCounts(
-                                5, "max_job_total_off_critical",
-                                1, "max_job_max_bg_off_critical",
-                                1, "max_job_min_bg_off_critical"));
+                                5, KEY_PREFIX_MAX_JOB + "total_off_critical",
+                                1, KEY_PREFIX_MAX_JOB + "max_bg_off_critical",
+                                1, KEY_PREFIX_MAX_JOB + "min_bg_off_critical"));
 
 
         /** Wait for this long after screen off before increasing the job concurrency. */
-        final KeyValueListParser.IntValue SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS =
-                new KeyValueListParser.IntValue(
-                        "screen_off_job_concurrency_increase_delay_ms", 30_000);
+        long SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS =
+                DEFAULT_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS;
 
         /**
          * The minimum backoff time to allow for linear backoff.
          */
-        long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
+        long MIN_LINEAR_BACKOFF_TIME_MS = DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS;
         /**
          * The minimum backoff time to allow for exponential backoff.
          */
-        long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
+        long MIN_EXP_BACKOFF_TIME_MS = DEFAULT_MIN_EXP_BACKOFF_TIME_MS;
 
         /**
          * The fraction of a job's running window that must pass before we
@@ -652,61 +670,78 @@
         public boolean API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT =
                 DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT;
 
-        private final KeyValueListParser mParser = new KeyValueListParser(',');
-
-        void updateConstantsLocked(String value) {
-            try {
-                mParser.setString(value);
-            } catch (Exception e) {
-                // Failed to parse the settings string, log this and move on
-                // with defaults.
-                Slog.e(TAG, "Bad jobscheduler settings", e);
-            }
-
-            MIN_READY_NON_ACTIVE_JOBS_COUNT = mParser.getInt(
+        private void updateBatchingConstantsLocked() {
+            MIN_READY_NON_ACTIVE_JOBS_COUNT = DeviceConfig.getInt(
+                    DeviceConfig.NAMESPACE_JOB_SCHEDULER,
                     KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT,
                     DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT);
-            MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = mParser.getLong(
+            MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = DeviceConfig.getLong(
+                    DeviceConfig.NAMESPACE_JOB_SCHEDULER,
                     KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS,
                     DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS);
-            HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
+        }
+
+        private void updateUseFactorConstantsLocked() {
+            HEAVY_USE_FACTOR = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                    KEY_HEAVY_USE_FACTOR,
                     DEFAULT_HEAVY_USE_FACTOR);
-            MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
+            MODERATE_USE_FACTOR = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                    KEY_MODERATE_USE_FACTOR,
                     DEFAULT_MODERATE_USE_FACTOR);
+        }
 
-            MAX_JOB_COUNTS_SCREEN_ON.normal.parse(mParser);
-            MAX_JOB_COUNTS_SCREEN_ON.moderate.parse(mParser);
-            MAX_JOB_COUNTS_SCREEN_ON.low.parse(mParser);
-            MAX_JOB_COUNTS_SCREEN_ON.critical.parse(mParser);
+        void updateConcurrencyConstantsLocked() {
+            MAX_JOB_COUNTS_SCREEN_ON.normal.update();
+            MAX_JOB_COUNTS_SCREEN_ON.moderate.update();
+            MAX_JOB_COUNTS_SCREEN_ON.low.update();
+            MAX_JOB_COUNTS_SCREEN_ON.critical.update();
 
-            MAX_JOB_COUNTS_SCREEN_OFF.normal.parse(mParser);
-            MAX_JOB_COUNTS_SCREEN_OFF.moderate.parse(mParser);
-            MAX_JOB_COUNTS_SCREEN_OFF.low.parse(mParser);
-            MAX_JOB_COUNTS_SCREEN_OFF.critical.parse(mParser);
+            MAX_JOB_COUNTS_SCREEN_OFF.normal.update();
+            MAX_JOB_COUNTS_SCREEN_OFF.moderate.update();
+            MAX_JOB_COUNTS_SCREEN_OFF.low.update();
+            MAX_JOB_COUNTS_SCREEN_OFF.critical.update();
 
-            SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.parse(mParser);
+            SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS = DeviceConfig.getLong(
+                    DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                    KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS,
+                    DEFAULT_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
+        }
 
-            MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
-                    DEFAULT_MIN_LINEAR_BACKOFF_TIME);
-            MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
-                    DEFAULT_MIN_EXP_BACKOFF_TIME);
-            CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC,
+        private void updateBackoffConstantsLocked() {
+            MIN_LINEAR_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                    KEY_MIN_LINEAR_BACKOFF_TIME_MS,
+                    DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS);
+            MIN_EXP_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                    KEY_MIN_EXP_BACKOFF_TIME_MS,
+                    DEFAULT_MIN_EXP_BACKOFF_TIME_MS);
+        }
+
+        private void updateConnectivityConstantsLocked() {
+            CONN_CONGESTION_DELAY_FRAC = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                    KEY_CONN_CONGESTION_DELAY_FRAC,
                     DEFAULT_CONN_CONGESTION_DELAY_FRAC);
-            CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
+            CONN_PREFETCH_RELAX_FRAC = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                    KEY_CONN_PREFETCH_RELAX_FRAC,
                     DEFAULT_CONN_PREFETCH_RELAX_FRAC);
+        }
 
-            ENABLE_API_QUOTAS = mParser.getBoolean(KEY_ENABLE_API_QUOTAS,
-                DEFAULT_ENABLE_API_QUOTAS);
+        private void updateApiQuotaConstantsLocked() {
+            ENABLE_API_QUOTAS = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                    KEY_ENABLE_API_QUOTAS, DEFAULT_ENABLE_API_QUOTAS);
             // Set a minimum value on the quota limit so it's not so low that it interferes with
             // legitimate use cases.
             API_QUOTA_SCHEDULE_COUNT = Math.max(250,
-                    mParser.getInt(KEY_API_QUOTA_SCHEDULE_COUNT, DEFAULT_API_QUOTA_SCHEDULE_COUNT));
-            API_QUOTA_SCHEDULE_WINDOW_MS = mParser.getDurationMillis(
+                    DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+                            KEY_API_QUOTA_SCHEDULE_COUNT, DEFAULT_API_QUOTA_SCHEDULE_COUNT));
+            API_QUOTA_SCHEDULE_WINDOW_MS = DeviceConfig.getLong(
+                    DeviceConfig.NAMESPACE_JOB_SCHEDULER,
                 KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS);
-            API_QUOTA_SCHEDULE_THROW_EXCEPTION = mParser.getBoolean(
+            API_QUOTA_SCHEDULE_THROW_EXCEPTION = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_JOB_SCHEDULER,
                     KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
                     DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION);
-            API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = mParser.getBoolean(
+            API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_JOB_SCHEDULER,
                     KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT,
                     DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT);
         }
@@ -731,10 +766,11 @@
             MAX_JOB_COUNTS_SCREEN_OFF.low.dump(pw, "");
             MAX_JOB_COUNTS_SCREEN_OFF.critical.dump(pw, "");
 
-            SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dump(pw, "");
+            pw.print(KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS,
+                    SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS).println();
 
-            pw.print(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
-            pw.print(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
+            pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println();
+            pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println();
             pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
             pw.print(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
 
@@ -760,11 +796,11 @@
             MAX_JOB_COUNTS_SCREEN_ON.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_ON);
             MAX_JOB_COUNTS_SCREEN_OFF.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_OFF);
 
-            SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dumpProto(proto,
-                    ConstantsProto.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
+            proto.write(ConstantsProto.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS,
+                    SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
 
-            proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
-            proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
+            proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS);
+            proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS);
             proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
             proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
 
@@ -1407,7 +1443,7 @@
 
         mHandler = new JobHandler(context.getMainLooper());
         mConstants = new Constants();
-        mConstantsObserver = new ConstantsObserver(mHandler);
+        mConstantsObserver = new ConstantsObserver();
         mJobSchedulerStub = new JobSchedulerStub();
 
         mConcurrencyManager = new JobConcurrencyManager(this);
@@ -1521,7 +1557,7 @@
     @Override
     public void onBootPhase(int phase) {
         if (PHASE_SYSTEM_SERVICES_READY == phase) {
-            mConstantsObserver.start(getContext().getContentResolver());
+            mConstantsObserver.start();
             for (StateController controller : mControllers) {
                 controller.onSystemServicesReady();
             }
@@ -1693,8 +1729,8 @@
         switch (job.getBackoffPolicy()) {
             case JobInfo.BACKOFF_POLICY_LINEAR: {
                 long backoff = initialBackoffMillis;
-                if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
-                    backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
+                if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME_MS) {
+                    backoff = mConstants.MIN_LINEAR_BACKOFF_TIME_MS;
                 }
                 delayMillis = backoff * backoffAttempts;
             } break;
@@ -1704,8 +1740,8 @@
                 }
             case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
                 long backoff = initialBackoffMillis;
-                if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
-                    backoff = mConstants.MIN_EXP_BACKOFF_TIME;
+                if (backoff < mConstants.MIN_EXP_BACKOFF_TIME_MS) {
+                    backoff = mConstants.MIN_EXP_BACKOFF_TIME_MS;
                 }
                 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
             } break;
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 6bc95bf..c033138 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -2131,7 +2131,7 @@
         }
 
         public List<UserHandle> getValidCrossProfileTargets(String pkg, int userId) {
-            final int uid = mPackageManagerInternal.getPackageUidInternal(pkg, 0, userId);
+            final int uid = mPackageManagerInternal.getPackageUid(pkg, /* flags= */ 0, userId);
             final AndroidPackage aPkg = mPackageManagerInternal.getPackage(uid);
             if (uid < 0
                     || aPkg == null
diff --git a/api/current.txt b/api/current.txt
index 9e0d88e..e5baa7c 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11617,6 +11617,16 @@
     field public int version;
   }
 
+  public final class FileChecksum implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getKind();
+    method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
+    method @Nullable public String getSplitName();
+    method @NonNull public byte[] getValue();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.FileChecksum> CREATOR;
+  }
+
   public final class InstallSourceInfo implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public String getInitiatingPackageName();
@@ -11901,8 +11911,8 @@
     field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
     field public static final int INVALID_ID = -1; // 0xffffffff
     field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
+    field public static final int STAGED_SESSION_CONFLICT = 4; // 0x4
     field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
-    field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
     field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
     field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
   }
@@ -11992,6 +12002,7 @@
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public CharSequence getBackgroundPermissionOptionLabel();
     method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
+    method public void getChecksums(@NonNull String, boolean, int, @Nullable java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
     method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
@@ -12082,6 +12093,7 @@
     field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
     field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
     field public static final int DONT_KILL_APP = 1; // 0x1
+    field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
     field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
     field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
     field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
@@ -12236,6 +12248,8 @@
     field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
     field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
+    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
+    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
     field public static final int PERMISSION_DENIED = -1; // 0xffffffff
     field public static final int PERMISSION_GRANTED = 0; // 0x0
     field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
@@ -12245,9 +12259,16 @@
     field public static final int SIGNATURE_SECOND_NOT_SIGNED = -2; // 0xfffffffe
     field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc
     field public static final int SYNCHRONOUS = 2; // 0x2
+    field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL;
+    field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE;
     field public static final int VERIFICATION_ALLOW = 1; // 0x1
     field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
     field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
+    field public static final int WHOLE_MD5 = 2; // 0x2
+    field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
+    field public static final int WHOLE_SHA1 = 4; // 0x4
+    field public static final int WHOLE_SHA256 = 8; // 0x8
+    field public static final int WHOLE_SHA512 = 16; // 0x10
   }
 
   public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
@@ -14250,6 +14271,10 @@
     enum_constant public static final android.graphics.BlurMaskFilter.Blur SOLID;
   }
 
+  public final class BlurShader extends android.graphics.Shader {
+    ctor public BlurShader(float, float, @Nullable android.graphics.Shader);
+  }
+
   public class Camera {
     ctor public Camera();
     method public void applyToCanvas(android.graphics.Canvas);
@@ -31620,6 +31645,7 @@
     method @Nullable public String getPassphrase();
     method @Nullable public android.net.wifi.hotspot2.PasspointConfiguration getPasspointConfig();
     method @IntRange(from=0) public int getPriority();
+    method public int getPriorityGroup();
     method @Nullable public String getSsid();
     method public boolean isAppInteractionRequired();
     method public boolean isCredentialSharedWithUser();
@@ -31646,6 +31672,7 @@
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsUserInteractionRequired(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPasspointConfig(@NonNull android.net.wifi.hotspot2.PasspointConfiguration);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPriority(@IntRange(from=0) int);
+    method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPriorityGroup(int);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setSsid(@NonNull String);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setUntrusted(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWapiEnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
@@ -36733,9 +36760,11 @@
   public final class PowerManager {
     method public void addThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener);
     method public void addThermalStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.PowerManager.OnThermalStatusChangedListener);
+    method @Nullable public java.time.Duration getBatteryDischargePrediction();
     method public int getCurrentThermalStatus();
     method public int getLocationPowerSaveMode();
     method public float getThermalHeadroom(@IntRange(from=0, to=60) int);
+    method public boolean isBatteryDischargePredictionPersonalized();
     method public boolean isDeviceIdleMode();
     method public boolean isIgnoringBatteryOptimizations(String);
     method public boolean isInteractive();
@@ -47836,6 +47865,7 @@
     method public void onDataConnectionStateChanged(int);
     method public void onDataConnectionStateChanged(int, int);
     method @RequiresPermission("android.permission.READ_PHONE_STATE") public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
+    method public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
     method public void onMessageWaitingIndicatorChanged(boolean);
     method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
@@ -48352,7 +48382,9 @@
     method @Deprecated public String iccTransmitApduBasicChannel(int, int, int, int, int, String);
     method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
     method public boolean isConcurrentVoiceAndDataSupported();
+    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isDataConnectionAllowed();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled();
+    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledForReason(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
     method public boolean isEmergencyNumber(@NonNull String);
     method public boolean isHearingAidCompatibilitySupported();
@@ -48374,6 +48406,7 @@
     method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
     method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledForReason(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setForbiddenPlmns(@NonNull java.util.List<java.lang.String>);
     method public boolean setLine1NumberForDisplay(String, String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setNetworkSelectionModeAutomatic();
@@ -48421,6 +48454,10 @@
     field public static final int DATA_CONNECTING = 1; // 0x1
     field public static final int DATA_DISCONNECTED = 0; // 0x0
     field public static final int DATA_DISCONNECTING = 4; // 0x4
+    field public static final int DATA_ENABLED_REASON_CARRIER = 2; // 0x2
+    field public static final int DATA_ENABLED_REASON_POLICY = 1; // 0x1
+    field public static final int DATA_ENABLED_REASON_THERMAL = 3; // 0x3
+    field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
     field public static final int DATA_SUSPENDED = 3; // 0x3
     field public static final int DATA_UNKNOWN = -1; // 0xffffffff
     field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
@@ -55185,7 +55222,7 @@
   }
 
   public class ViewPropertyAnimator {
-    method public android.view.ViewPropertyAnimator alpha(float);
+    method public android.view.ViewPropertyAnimator alpha(@FloatRange(from=0.0f, to=1.0f) float);
     method public android.view.ViewPropertyAnimator alphaBy(float);
     method public void cancel();
     method public long getDuration();
diff --git a/api/system-current.txt b/api/system-current.txt
index 3ab1645..00a59bf 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -219,6 +219,7 @@
     field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
     field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
     field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
+    field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
     field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
     field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
     field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON";
@@ -8394,6 +8395,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressedForToken(@NonNull String);
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSaveEnabled(boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
     method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setDynamicPowerSaveHint(boolean, int);
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveModeEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void suppressAmbientDisplay(@NonNull String, boolean);
@@ -10816,7 +10818,8 @@
 
   public class PhoneStateListener {
     method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
-    method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
+    method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
+    method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
     method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
     method public void onRadioPowerStateChanged(int);
@@ -11233,10 +11236,8 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionAllowed();
     method public boolean isDataConnectivityPossible();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
-    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledWithReason(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
@@ -11268,7 +11269,6 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledWithReason(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
@@ -11306,10 +11306,6 @@
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
     field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
-    field public static final int DATA_ENABLED_REASON_CARRIER = 2; // 0x2
-    field public static final int DATA_ENABLED_REASON_POLICY = 1; // 0x1
-    field public static final int DATA_ENABLED_REASON_THERMAL = 3; // 0x3
-    field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
     field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
     field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
     field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
@@ -12744,8 +12740,8 @@
   public interface PacProcessor {
     method @Nullable public String findProxyForUrl(@NonNull String);
     method @NonNull public static android.webkit.PacProcessor getInstance();
-    method @NonNull public static android.webkit.PacProcessor getInstanceForNetwork(long);
-    method public default long getNetworkHandle();
+    method @NonNull public static android.webkit.PacProcessor getInstanceForNetwork(@Nullable android.net.Network);
+    method @Nullable public default android.net.Network getNetwork();
     method public default void releasePacProcessor();
     method public boolean setProxyScript(@NonNull String);
   }
@@ -12886,7 +12882,7 @@
     method public android.webkit.CookieManager getCookieManager();
     method public android.webkit.GeolocationPermissions getGeolocationPermissions();
     method @NonNull public default android.webkit.PacProcessor getPacProcessor();
-    method @NonNull public default android.webkit.PacProcessor getPacProcessorForNetwork(long);
+    method @NonNull public default android.webkit.PacProcessor getPacProcessorForNetwork(@Nullable android.net.Network);
     method public android.webkit.ServiceWorkerController getServiceWorkerController();
     method public android.webkit.WebViewFactoryProvider.Statics getStatics();
     method @Deprecated public android.webkit.TokenBindingService getTokenBindingService();
diff --git a/api/test-current.txt b/api/test-current.txt
index 342bd81..529dcf7 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -2799,8 +2799,10 @@
 
   public final class PowerManager {
     method @RequiresPermission("android.permission.POWER_SAVER") public int getPowerSaveModeTrigger();
+    method @RequiresPermission("android.permission.DEVICE_POWER") public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
     method @RequiresPermission("android.permission.POWER_SAVER") public boolean setDynamicPowerSaveHint(boolean, int);
     method @RequiresPermission(anyOf={"android.permission.DEVICE_POWER", "android.permission.POWER_SAVER"}) public boolean setPowerSaveModeEnabled(boolean);
+    field public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED = "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED";
     field public static final int POWER_SAVE_MODE_TRIGGER_DYNAMIC = 1; // 0x1
     field public static final int POWER_SAVE_MODE_TRIGGER_PERCENTAGE = 0; // 0x0
   }
@@ -3238,6 +3240,7 @@
     field public static final String NAMESPACE_AUTOFILL = "autofill";
     field public static final String NAMESPACE_BIOMETRICS = "biometrics";
     field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture";
+    field public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
     field public static final String NAMESPACE_PERMISSIONS = "permissions";
     field public static final String NAMESPACE_PRIVACY = "privacy";
     field public static final String NAMESPACE_ROLLBACK = "rollback";
@@ -4073,7 +4076,8 @@
   }
 
   public class PhoneStateListener {
-    method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
+    method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
+    method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
     method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
     field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000
     field @RequiresPermission("android.permission.READ_ACTIVE_EMERGENCY_SESSION") public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000
@@ -5574,7 +5578,7 @@
     method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl);
     method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo);
     method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void registerOrganizer(int);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void registerOrganizer();
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setInterceptBackPressedOnTaskRoot(boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public static void setLaunchRoot(int, @NonNull android.window.WindowContainerToken);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void unregisterOrganizer();
diff --git a/cmds/idmap2/tests/ResultTests.cpp b/cmds/idmap2/tests/ResultTests.cpp
index cbced0a..f2f8854 100644
--- a/cmds/idmap2/tests/ResultTests.cpp
+++ b/cmds/idmap2/tests/ResultTests.cpp
@@ -260,7 +260,8 @@
 
 struct NoCopyContainer {
   uint32_t value;  // NOLINT(misc-non-private-member-variables-in-classes)
-  DISALLOW_COPY_AND_ASSIGN(NoCopyContainer);
+  NoCopyContainer(const NoCopyContainer&) = delete;
+  NoCopyContainer& operator=(const NoCopyContainer&) = delete;
 };
 
 Result<std::unique_ptr<NoCopyContainer>> CreateNoCopyContainer(bool succeed) {
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index dc16125..13bf197 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -554,6 +554,10 @@
             return NO_ERROR;
         }
         if (!args[0].compare(String8("section"))) {
+            if (argCount == 1) {
+                fprintf(out, "Not enough arguments for section\n");
+                return NO_ERROR;
+            }
             int id = atoi(args[1]);
             int idx = 0;
             while (SECTION_LIST[idx] != NULL) {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 8b2c2da..e6e22ba 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -4007,7 +4007,7 @@
     optional bool is_group_summary = 5;
 
     // The section of the shade that the notification is in.
-    // See NotificationSectionsManager.PriorityBucket.
+    // See SystemUI Notifications.proto.
     enum NotificationSection {
         SECTION_UNKNOWN = 0;
         SECTION_HEADS_UP = 1;
@@ -4015,6 +4015,7 @@
         SECTION_PEOPLE = 3;
         SECTION_ALERTING = 4;
         SECTION_SILENT = 5;
+        SECTION_FOREGROUND_SERVICE = 6;
     }
     optional NotificationSection section = 6;
 }
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index 3b65f82..4574b2e 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -25,8 +25,9 @@
 using std::unordered_map;
 using std::vector;
 
-CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index)
-    : ConditionTracker(id, index) {
+CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index,
+                                                         const uint64_t protoHash)
+    : ConditionTracker(id, index, protoHash) {
     VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId);
 }
 
@@ -122,6 +123,49 @@
     return true;
 }
 
+bool CombinationConditionTracker::onConfigUpdated(
+        const vector<Predicate>& allConditionProtos, const int index,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& conditionTrackerMap) {
+    ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers,
+                                      atomMatchingTrackerMap, conditionTrackerMap);
+    mTrackerIndex.clear();
+    mChildren.clear();
+    mUnSlicedChildren.clear();
+    mSlicedChildren.clear();
+    Predicate_Combination combinationCondition = allConditionProtos[mIndex].combination();
+
+    for (const int64_t child : combinationCondition.predicate()) {
+        const auto& it = conditionTrackerMap.find(child);
+
+        if (it == conditionTrackerMap.end()) {
+            ALOGW("Predicate %lld not found in the config", (long long)child);
+            return false;
+        }
+
+        int childIndex = it->second;
+        const sp<ConditionTracker>& childTracker = allConditionTrackers[childIndex];
+
+        // Ensures that the child's tracker indices are updated.
+        if (!childTracker->onConfigUpdated(allConditionProtos, childIndex, allConditionTrackers,
+                                           atomMatchingTrackerMap, conditionTrackerMap)) {
+            ALOGW("Child update failed %lld ", (long long)child);
+            return false;
+        }
+
+        if (allConditionTrackers[childIndex]->isSliced()) {
+            mSlicedChildren.push_back(childIndex);
+        } else {
+            mUnSlicedChildren.push_back(childIndex);
+        }
+        mChildren.push_back(childIndex);
+        mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(),
+                             childTracker->getAtomMatchingTrackerIndex().end());
+    }
+    return true;
+}
+
 void CombinationConditionTracker::isConditionMet(
         const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
         const bool isPartialLink,
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index a7fac3d..672d61c 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -24,9 +24,9 @@
 namespace os {
 namespace statsd {
 
-class CombinationConditionTracker : public virtual ConditionTracker {
+class CombinationConditionTracker : public ConditionTracker {
 public:
-    CombinationConditionTracker(const int64_t& id, const int index);
+    CombinationConditionTracker(const int64_t& id, const int index, const uint64_t protoHash);
 
     ~CombinationConditionTracker();
 
@@ -35,6 +35,11 @@
               const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
               std::vector<ConditionState>& conditionCache) override;
 
+    bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
+                         const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                         const std::unordered_map<int64_t, int>& conditionTrackerMap) override;
+
     void evaluateCondition(const LogEvent& event,
                            const std::vector<MatchingState>& eventMatcherValues,
                            const std::vector<sp<ConditionTracker>>& mAllConditions,
@@ -102,6 +107,7 @@
     std::vector<int> mSlicedChildren;
     std::vector<int> mUnSlicedChildren;
 
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 4e12535..3bf4e63 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -31,18 +31,17 @@
 
 class ConditionTracker : public virtual RefBase {
 public:
-    ConditionTracker(const int64_t& id, const int index)
+    ConditionTracker(const int64_t& id, const int index, const uint64_t protoHash)
         : mConditionId(id),
           mIndex(index),
           mInitialized(false),
           mTrackerIndex(),
           mUnSlicedPartCondition(ConditionState::kUnknown),
-          mSliced(false){};
+          mSliced(false),
+          mProtoHash(protoHash){};
 
     virtual ~ConditionTracker(){};
 
-    inline const int64_t& getId() { return mConditionId; }
-
     // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also
     // be done in the constructor, but we do it separately because (1) easy to return a bool to
     // indicate whether the initialization is successful. (2) makes unit test easier.
@@ -50,7 +49,7 @@
     // fill the condition cache with the current condition.
     // allConditionConfig: the list of all Predicate config from statsd_config.
     // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
-    //                       need to call init() on children conditions)
+    //                       need to call init() on child conditions)
     // conditionIdIndexMap: the mapping from condition id to its index.
     // stack: a bit map to keep track which nodes have been visited on the stack in the recursion.
     // conditionCache: tracks initial conditions of all ConditionTrackers. returns the
@@ -60,6 +59,26 @@
                       const std::unordered_map<int64_t, int>& conditionIdIndexMap,
                       std::vector<bool>& stack, std::vector<ConditionState>& conditionCache) = 0;
 
+    // Update appropriate state on config updates. Primarily, all indices need to be updated.
+    // This predicate and all of its children are guaranteed to be preserved across the update.
+    // This function is recursive and will call onConfigUpdated on child conditions. It does not
+    // manage cycle detection since all preserved conditions should not have any cycles.
+    //
+    // allConditionProtos: the new predicates.
+    // index: the new index of this tracker in allConditionProtos and allConditionTrackers.
+    // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
+    //                       need to call onConfigUpdated() on child conditions)
+    // [atomMatchingTrackerMap]: map of atom matcher id to index after the config update
+    // [conditionTrackerMap]: map of condition tracker id to index after the config update.
+    // returns whether or not the update is successful
+    virtual bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
+                                 const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                                 const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                                 const std::unordered_map<int64_t, int>& conditionTrackerMap) {
+        mIndex = index;
+        return true;
+    }
+
     // evaluate current condition given the new event.
     // event: the new log event
     // eventMatcherValues: the results of the AtomMatchingTrackers. AtomMatchingTrackers always
@@ -112,6 +131,10 @@
         return mConditionId;
     }
 
+    inline uint64_t getProtoHash() const {
+        return mProtoHash;
+    }
+
     virtual void getTrueSlicedDimensions(
         const std::vector<sp<ConditionTracker>>& allConditions,
         std::set<HashableDimensionKey>* dimensions) const = 0;
@@ -133,7 +156,7 @@
     const int64_t mConditionId;
 
     // the index of this condition in the manager's condition list.
-    const int mIndex;
+    int mIndex;
 
     // if it's properly initialized.
     bool mInitialized;
@@ -151,6 +174,12 @@
     ConditionState mUnSlicedPartCondition;
 
     bool mSliced;
+
+    // Hash of the Predicate's proto bytes from StatsdConfig.
+    // Used to determine if the definition of this condition has changed across a config update.
+    const uint64_t mProtoHash;
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index f45759b..1dcc8f9 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -27,54 +27,21 @@
 using std::unordered_map;
 
 SimpleConditionTracker::SimpleConditionTracker(
-        const ConfigKey& key, const int64_t& id, const int index,
+        const ConfigKey& key, const int64_t& id, const uint64_t protoHash, const int index,
         const SimplePredicate& simplePredicate,
-        const unordered_map<int64_t, int>& trackerNameIndexMap)
-    : ConditionTracker(id, index), mConfigKey(key), mContainANYPositionInInternalDimensions(false) {
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap)
+    : ConditionTracker(id, index, protoHash),
+      mConfigKey(key),
+      mContainANYPositionInInternalDimensions(false) {
     VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId);
     mCountNesting = simplePredicate.count_nesting();
 
-    if (simplePredicate.has_start()) {
-        auto pair = trackerNameIndexMap.find(simplePredicate.start());
-        if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
-            return;
-        }
-        mStartLogMatcherIndex = pair->second;
-        mTrackerIndex.insert(mStartLogMatcherIndex);
-    } else {
-        mStartLogMatcherIndex = -1;
-    }
-
-    if (simplePredicate.has_stop()) {
-        auto pair = trackerNameIndexMap.find(simplePredicate.stop());
-        if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop());
-            return;
-        }
-        mStopLogMatcherIndex = pair->second;
-        mTrackerIndex.insert(mStopLogMatcherIndex);
-    } else {
-        mStopLogMatcherIndex = -1;
-    }
-
-    if (simplePredicate.has_stop_all()) {
-        auto pair = trackerNameIndexMap.find(simplePredicate.stop_all());
-        if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Stop all matcher %lld found in the config", (long long)simplePredicate.stop_all());
-            return;
-        }
-        mStopAllLogMatcherIndex = pair->second;
-        mTrackerIndex.insert(mStopAllLogMatcherIndex);
-    } else {
-        mStopAllLogMatcherIndex = -1;
-    }
+    setMatcherIndices(simplePredicate, atomMatchingTrackerMap);
 
     if (simplePredicate.has_dimensions()) {
         translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions);
         if (mOutputDimensions.size() > 0) {
             mSliced = true;
-            mDimensionTag = mOutputDimensions[0].mMatcher.getTag();
         }
         mContainANYPositionInInternalDimensions = HasPositionANY(simplePredicate.dimensions());
     }
@@ -106,6 +73,59 @@
     return mInitialized;
 }
 
+bool SimpleConditionTracker::onConfigUpdated(
+        const vector<Predicate>& allConditionProtos, const int index,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& conditionTrackerMap) {
+    ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers,
+                                      atomMatchingTrackerMap, conditionTrackerMap);
+    setMatcherIndices(allConditionProtos[index].simple_predicate(), atomMatchingTrackerMap);
+    return true;
+}
+
+void SimpleConditionTracker::setMatcherIndices(
+        const SimplePredicate& simplePredicate,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+    mTrackerIndex.clear();
+    if (simplePredicate.has_start()) {
+        auto pair = atomMatchingTrackerMap.find(simplePredicate.start());
+        if (pair == atomMatchingTrackerMap.end()) {
+            ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
+            return;
+        }
+        mStartLogMatcherIndex = pair->second;
+        mTrackerIndex.insert(mStartLogMatcherIndex);
+    } else {
+        mStartLogMatcherIndex = -1;
+    }
+
+    if (simplePredicate.has_stop()) {
+        auto pair = atomMatchingTrackerMap.find(simplePredicate.stop());
+        if (pair == atomMatchingTrackerMap.end()) {
+            ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop());
+            return;
+        }
+        mStopLogMatcherIndex = pair->second;
+        mTrackerIndex.insert(mStopLogMatcherIndex);
+    } else {
+        mStopLogMatcherIndex = -1;
+    }
+
+    if (simplePredicate.has_stop_all()) {
+        auto pair = atomMatchingTrackerMap.find(simplePredicate.stop_all());
+        if (pair == atomMatchingTrackerMap.end()) {
+            ALOGW("Stop all matcher %lld found in the config",
+                  (long long)simplePredicate.stop_all());
+            return;
+        }
+        mStopAllLogMatcherIndex = pair->second;
+        mTrackerIndex.insert(mStopAllLogMatcherIndex);
+    } else {
+        mStopAllLogMatcherIndex = -1;
+    }
+}
+
 void SimpleConditionTracker::dumpState() {
     VLOG("%lld DUMP:", (long long)mConditionId);
     for (const auto& pair : mSlicedConditionState) {
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index 1a9e35e..7a8b4010 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -27,11 +27,11 @@
 namespace os {
 namespace statsd {
 
-class SimpleConditionTracker : public virtual ConditionTracker {
+class SimpleConditionTracker : public ConditionTracker {
 public:
-    SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const int index,
-                           const SimplePredicate& simplePredicate,
-                           const std::unordered_map<int64_t, int>& trackerNameIndexMap);
+    SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const uint64_t protoHash,
+                           const int index, const SimplePredicate& simplePredicate,
+                           const std::unordered_map<int64_t, int>& atomMatchingTrackerMap);
 
     ~SimpleConditionTracker();
 
@@ -40,6 +40,11 @@
               const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
               std::vector<ConditionState>& conditionCache) override;
 
+    bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
+                         const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                         const std::unordered_map<int64_t, int>& conditionTrackerMap) override;
+
     void evaluateCondition(const LogEvent& event,
                            const std::vector<MatchingState>& eventMatcherValues,
                            const std::vector<sp<ConditionTracker>>& mAllConditions,
@@ -112,10 +117,11 @@
     std::set<HashableDimensionKey> mLastChangedToTrueDimensions;
     std::set<HashableDimensionKey> mLastChangedToFalseDimensions;
 
-    int mDimensionTag;
-
     std::map<HashableDimensionKey, int> mSlicedConditionState;
 
+    void setMatcherIndices(const SimplePredicate& predicate,
+                           const std::unordered_map<int64_t, int>& logTrackerMap);
+
     void handleStopAll(std::vector<ConditionState>& conditionCache,
                        std::vector<bool>& changedCache);
 
@@ -129,6 +135,7 @@
     FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
     FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
     FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/matchers/AtomMatchingTracker.h b/cmds/statsd/src/matchers/AtomMatchingTracker.h
index 5194f99..c138497 100644
--- a/cmds/statsd/src/matchers/AtomMatchingTracker.h
+++ b/cmds/statsd/src/matchers/AtomMatchingTracker.h
@@ -52,6 +52,15 @@
                       const std::unordered_map<int64_t, int>& matcherMap,
                       std::vector<bool>& stack) = 0;
 
+    // Update appropriate state on config updates. Primarily, all indices need to be updated.
+    // This matcher and all of its children are guaranteed to be preserved across the update.
+    // matcher: the AtomMatcher proto from the config.
+    // index: the index of this matcher in mAllAtomMatchingTrackers.
+    // atomMatchingTrackerMap: map from matcher id to index in mAllAtomMatchingTrackers
+    virtual bool onConfigUpdated(
+            const AtomMatcher& matcher, const int index,
+            const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) = 0;
+
     // Called when a log event comes.
     // event: the log event.
     // allAtomMatchingTrackers: the list of all AtomMatchingTrackers. This is needed because the log
@@ -83,7 +92,7 @@
     const int64_t mId;
 
     // Index of this AtomMatchingTracker in MetricsManager's container.
-    const int mIndex;
+    int mIndex;
 
     // Whether this AtomMatchingTracker has been properly initialized.
     bool mInitialized;
diff --git a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp
index 19637a4..45685ce 100644
--- a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.cpp
@@ -93,6 +93,23 @@
     return true;
 }
 
+bool CombinationAtomMatchingTracker::onConfigUpdated(
+        const AtomMatcher& matcher, const int index,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+    mIndex = index;
+    mChildren.clear();
+    AtomMatcher_Combination combinationMatcher = matcher.combination();
+    for (const int64_t child : combinationMatcher.matcher()) {
+        const auto& pair = atomMatchingTrackerMap.find(child);
+        if (pair == atomMatchingTrackerMap.end()) {
+            ALOGW("Matcher %lld not found in the config", (long long)child);
+            return false;
+        }
+        mChildren.push_back(pair->second);
+    }
+    return true;
+}
+
 void CombinationAtomMatchingTracker::onLogEvent(
         const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
         vector<MatchingState>& matcherResults) {
diff --git a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h
index 06a4932..3160448 100644
--- a/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h
+++ b/cmds/statsd/src/matchers/CombinationAtomMatchingTracker.h
@@ -27,7 +27,7 @@
 namespace statsd {
 
 // Represents a AtomMatcher_Combination in the StatsdConfig.
-class CombinationAtomMatchingTracker : public virtual AtomMatchingTracker {
+class CombinationAtomMatchingTracker : public AtomMatchingTracker {
 public:
     CombinationAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash);
 
@@ -35,6 +35,9 @@
               const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
               const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack);
 
+    bool onConfigUpdated(const AtomMatcher& matcher, const int index,
+                         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override;
+
     ~CombinationAtomMatchingTracker();
 
     void onLogEvent(const LogEvent& event,
@@ -45,6 +48,8 @@
     LogicalOperation mLogicalOperation;
 
     std::vector<int> mChildren;
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateMatchers);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp
index 86b148d..423da5b 100644
--- a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.cpp
@@ -50,6 +50,14 @@
     return mInitialized;
 }
 
+bool SimpleAtomMatchingTracker::onConfigUpdated(
+        const AtomMatcher& matcher, const int index,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+    mIndex = index;
+    // Do not need to update mMatcher since the matcher must be identical across the update.
+    return mInitialized;
+}
+
 void SimpleAtomMatchingTracker::onLogEvent(
         const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
         vector<MatchingState>& matcherResults) {
diff --git a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h
index 49cbe09..b67e6c2 100644
--- a/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h
+++ b/cmds/statsd/src/matchers/SimpleAtomMatchingTracker.h
@@ -28,7 +28,7 @@
 namespace os {
 namespace statsd {
 
-class SimpleAtomMatchingTracker : public virtual AtomMatchingTracker {
+class SimpleAtomMatchingTracker : public AtomMatchingTracker {
 public:
     SimpleAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash,
                               const SimpleAtomMatcher& matcher, const sp<UidMap>& uidMap);
@@ -40,6 +40,9 @@
               const std::unordered_map<int64_t, int>& matcherMap,
               std::vector<bool>& stack) override;
 
+    bool onConfigUpdated(const AtomMatcher& matcher, const int index,
+                         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override;
+
     void onLogEvent(const LogEvent& event,
                     const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
                     std::vector<MatchingState>& matcherResults) override;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 5a52032..a0c701e 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -80,7 +80,7 @@
     mConfigValid = initStatsdConfig(
             key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
             timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap,
-            mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers,
+            mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mAllAnomalyTrackers,
             mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap,
             mTrackerToConditionMap, mActivationAtomTrackerToMetricMap,
             mDeactivationAtomTrackerToMetricMap, mAlertTrackerMap, mMetricIndexesWithActivation,
@@ -204,13 +204,20 @@
                                   const sp<AlarmMonitor>& periodicAlarmMonitor) {
     vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers;
     unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    vector<sp<ConditionTracker>> newConditionTrackers;
+    unordered_map<int64_t, int> newConditionTrackerMap;
     mTagIds.clear();
+    mTrackerToConditionMap.clear();
     mConfigValid = updateStatsdConfig(
             mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap, mTagIds,
-            newAtomMatchingTrackers, newAtomMatchingTrackerMap);
+            timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap,
+            mAllConditionTrackers, mConditionTrackerMap, mTagIds, newAtomMatchingTrackers,
+            newAtomMatchingTrackerMap, newConditionTrackers, newConditionTrackerMap,
+            mTrackerToConditionMap);
     mAllAtomMatchingTrackers = newAtomMatchingTrackers;
     mAtomMatchingTrackerMap = newAtomMatchingTrackerMap;
+    mAllConditionTrackers = newConditionTrackers;
+    mConditionTrackerMap = newConditionTrackerMap;
     return mConfigValid;
 }
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 6f4b2d7..bd0c816 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -242,9 +242,12 @@
     // To make updating configs faster, we map the id of a AtomMatchingTracker, MetricProducer, and
     // ConditionTracker to its index in the corresponding vector.
 
-    // Maps the id of an atom matcher to its index in mAllAtomMatchingTrackers.
+    // Maps the id of an atom matching tracker to its index in mAllAtomMatchingTrackers.
     std::unordered_map<int64_t, int> mAtomMatchingTrackerMap;
 
+    // Maps the id of a condition tracker to its index in mAllConditionTrackers.
+    std::unordered_map<int64_t, int> mConditionTrackerMap;
+
     // To make the log processing more efficient, we want to do as much filtering as possible
     // before we go into individual trackers and conditions to match.
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 9b684f1..dcfbd5e 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -411,7 +411,7 @@
 
 void ValueMetricProducer::resetBase() {
     for (auto& slice : mCurrentBaseInfo) {
-        for (auto& baseInfo : slice.second) {
+        for (auto& baseInfo : slice.second.baseInfos) {
             baseInfo.hasBase = false;
         }
     }
@@ -623,7 +623,7 @@
                 mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end();
         if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) {
             auto it = mCurrentBaseInfo.find(whatKey);
-            for (auto& baseInfo : it->second) {
+            for (auto& baseInfo : it->second.baseInfos) {
                 baseInfo.hasBase = false;
             }
         }
@@ -652,7 +652,7 @@
             (unsigned long)mCurrentSlicedBucket.size());
     if (verbose) {
         for (const auto& it : mCurrentSlicedBucket) {
-          for (const auto& interval : it.second) {
+          for (const auto& interval : it.second.intervals) {
               fprintf(out, "\t(what)%s\t(states)%s  (value)%s\n",
                       it.first.getDimensionKeyInWhat().toString().c_str(),
                       it.first.getStateValuesKey().toString().c_str(),
@@ -788,23 +788,23 @@
         return;
     }
 
-    vector<BaseInfo>& baseInfos = mCurrentBaseInfo[whatKey];
+    DimensionsInWhatInfo& dimensionsInWhatInfo = mCurrentBaseInfo[whatKey];
+    vector<BaseInfo>& baseInfos = dimensionsInWhatInfo.baseInfos;
     if (baseInfos.size() < mFieldMatchers.size()) {
         VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
         baseInfos.resize(mFieldMatchers.size());
     }
 
-    for (BaseInfo& baseInfo : baseInfos) {
-        if (!baseInfo.hasCurrentState) {
-            baseInfo.currentState = getUnknownStateKey();
-            baseInfo.hasCurrentState = true;
-        }
+    if (!dimensionsInWhatInfo.hasCurrentState) {
+        dimensionsInWhatInfo.currentState = getUnknownStateKey();
+        dimensionsInWhatInfo.hasCurrentState = true;
     }
 
     // We need to get the intervals stored with the previous state key so we can
     // close these value intervals.
-    const auto oldStateKey = baseInfos[0].currentState;
-    vector<Interval>& intervals = mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)];
+    const auto oldStateKey = dimensionsInWhatInfo.currentState;
+    vector<Interval>& intervals =
+            mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)].intervals;
     if (intervals.size() < mFieldMatchers.size()) {
         VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
         intervals.resize(mFieldMatchers.size());
@@ -818,14 +818,14 @@
     // Discussion here: http://ag/6124370.
     bool useAnomalyDetection = true;
 
+    dimensionsInWhatInfo.hasCurrentState = true;
+    dimensionsInWhatInfo.currentState = stateKey;
     for (int i = 0; i < (int)mFieldMatchers.size(); i++) {
         const Matcher& matcher = mFieldMatchers[i];
         BaseInfo& baseInfo = baseInfos[i];
         Interval& interval = intervals[i];
         interval.valueIndex = i;
         Value value;
-        baseInfo.hasCurrentState = true;
-        baseInfo.currentState = stateKey;
         if (!getDoubleOrLong(event, matcher, value)) {
             VLOG("Failed to get value %d from event %s", i, event.ToString().c_str());
             StatsdStats::getInstance().noteBadValueType(mMetricId);
@@ -990,7 +990,7 @@
         bool bucketHasData = false;
         // The current bucket is large enough to keep.
         for (const auto& slice : mCurrentSlicedBucket) {
-            ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second);
+            PastValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second.intervals);
             bucket.mConditionTrueNs = conditionTrueDuration;
             // it will auto create new vector of ValuebucketInfo if the key is not found.
             if (bucket.valueIndex.size() > 0) {
@@ -1030,9 +1030,9 @@
     mCurrentBucketNum += numBucketsForward;
 }
 
-ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
-                                                    const std::vector<Interval>& intervals) {
-    ValueBucket bucket;
+PastValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
+                                                        const std::vector<Interval>& intervals) {
+    PastValueBucket bucket;
     bucket.mBucketStartNs = mCurrentBucketStartTimeNs;
     bucket.mBucketEndNs = bucketEndTime;
     for (const auto& interval : intervals) {
@@ -1059,7 +1059,7 @@
     // Cleanup data structure to aggregate values.
     for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
         bool obsolete = true;
-        for (auto& interval : it->second) {
+        for (auto& interval : it->second.intervals) {
             interval.hasValue = false;
             interval.sampleSize = 0;
             if (interval.seenNewData) {
@@ -1107,7 +1107,7 @@
                     continue;
                 }
                 // TODO: fix this when anomaly can accept double values
-                auto& interval = slice.second[0];
+                auto& interval = slice.second.intervals[0];
                 if (interval.hasValue) {
                     mCurrentFullBucket[slice.first] += interval.value.long_value;
                 }
@@ -1126,7 +1126,7 @@
                 for (auto& tracker : mAnomalyTrackers) {
                     if (tracker != nullptr) {
                         // TODO: fix this when anomaly can accept double values
-                        auto& interval = slice.second[0];
+                        auto& interval = slice.second.intervals[0];
                         if (interval.hasValue) {
                             tracker->addPastBucket(slice.first, interval.value.long_value,
                                                    mCurrentBucketNum);
@@ -1139,7 +1139,7 @@
         // Accumulate partial bucket.
         for (const auto& slice : mCurrentSlicedBucket) {
             // TODO: fix this when anomaly can accept double values
-            auto& interval = slice.second[0];
+            auto& interval = slice.second.intervals[0];
             if (interval.hasValue) {
                 mCurrentFullBucket[slice.first] += interval.value.long_value;
             }
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index e72002e..472cc33 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -31,7 +31,7 @@
 namespace os {
 namespace statsd {
 
-struct ValueBucket {
+struct PastValueBucket {
     int64_t mBucketStartNs;
     int64_t mBucketEndNs;
     std::vector<int> valueIndex;
@@ -41,7 +41,6 @@
     int64_t mConditionTrueNs;
 };
 
-
 // Aggregates values within buckets.
 //
 // There are different events that might complete a bucket
@@ -173,7 +172,7 @@
     // if this is pulled metric
     const bool mIsPulled;
 
-    // internal state of an ongoing aggregation bucket.
+    // Tracks the value information of one value field.
     typedef struct {
         // Index in multi value aggregation.
         int valueIndex;
@@ -188,25 +187,40 @@
         bool seenNewData = false;
     } Interval;
 
+    // Internal state of an ongoing aggregation bucket.
+    typedef struct CurrentValueBucket {
+        // Value information for each value field of the metric.
+        std::vector<Interval> intervals;
+    } CurrentValueBucket;
+
+    // Holds base information for diffing values from one value field.
     typedef struct {
         // Holds current base value of the dimension. Take diff and update if necessary.
         Value base;
         // Whether there is a base to diff to.
         bool hasBase;
+    } BaseInfo;
+
+    // State key and base information for a specific DimensionsInWhat key.
+    typedef struct {
+        std::vector<BaseInfo> baseInfos;
         // Last seen state value(s).
         HashableDimensionKey currentState;
         // Whether this dimensions in what key has a current state key.
         bool hasCurrentState;
-    } BaseInfo;
+    } DimensionsInWhatInfo;
 
-    std::unordered_map<MetricDimensionKey, std::vector<Interval>> mCurrentSlicedBucket;
+    // Tracks the internal state in the ongoing aggregation bucket for each DimensionsInWhat
+    // key and StateValuesKey pair.
+    std::unordered_map<MetricDimensionKey, CurrentValueBucket> mCurrentSlicedBucket;
 
-    std::unordered_map<HashableDimensionKey, std::vector<BaseInfo>> mCurrentBaseInfo;
+    // Tracks current state key and base information for each DimensionsInWhat key.
+    std::unordered_map<HashableDimensionKey, DimensionsInWhatInfo> mCurrentBaseInfo;
 
     std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
-    std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
+    std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>> mPastBuckets;
 
     const int64_t mMinBucketSizeNs;
 
@@ -224,8 +238,8 @@
     void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
                           int64_t originalPullTimeNs, int64_t eventElapsedTimeNs);
 
-    ValueBucket buildPartialBucket(int64_t bucketEndTime,
-                                   const std::vector<Interval>& intervals);
+    PastValueBucket buildPartialBucket(int64_t bucketEndTime,
+                                       const std::vector<Interval>& intervals);
 
     void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs);
 
@@ -234,7 +248,7 @@
     // Reset diff base and mHasGlobalBase
     void resetBase();
 
-    static const size_t kBucketSize = sizeof(ValueBucket{});
+    static const size_t kBucketSize = sizeof(PastValueBucket{});
 
     const size_t mDimensionSoftLimit;
 
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index a9ae5a4..bd60b6b 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -15,6 +15,7 @@
  */
 
 #define DEBUG false  // STOPSHIP if true
+#include "Log.h"
 
 #include "config_update_utils.h"
 
@@ -44,7 +45,7 @@
     // Check if new matcher.
     const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id);
     if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) {
-        matchersToUpdate[matcherIdx] = UPDATE_REPLACE;
+        matchersToUpdate[matcherIdx] = UPDATE_NEW;
         return true;
     }
 
@@ -103,11 +104,13 @@
     return true;
 }
 
-bool updateAtomTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
-                        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
-                        const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
-                        set<int>& allTagIds, unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
-                        vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers) {
+bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
+                                const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                                const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+                                set<int>& allTagIds,
+                                unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+                                vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
+                                set<int64_t>& replacedMatchers) {
     const int atomMatcherCount = config.atom_matcher_size();
 
     vector<AtomMatcher> matcherProtos;
@@ -148,11 +151,19 @@
                           (long long)id);
                     return false;
                 }
-                const int oldIndex = oldAtomMatchingTrackerIt->second;
-                newAtomMatchingTrackers.push_back(oldAtomMatchingTrackers[oldIndex]);
+                const sp<AtomMatchingTracker>& tracker =
+                        oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second];
+                if (!tracker->onConfigUpdated(matcherProtos[i], i, newAtomMatchingTrackerMap)) {
+                    ALOGW("Config update failed for matcher %lld", (long long)id);
+                    return false;
+                }
+                newAtomMatchingTrackers.push_back(tracker);
                 break;
             }
-            case UPDATE_REPLACE: {
+            case UPDATE_REPLACE:
+                replacedMatchers.insert(id);
+                [[fallthrough]];  // Intentionally fallthrough to create the new matcher.
+            case UPDATE_NEW: {
                 sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, i, uidMap);
                 if (tracker == nullptr) {
                     return false;
@@ -182,6 +193,207 @@
     return true;
 }
 
+// Recursive function to determine if a condition needs to be updated. Populates conditionsToUpdate.
+// Returns whether the function was successful or not.
+bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx,
+                                    const unordered_map<int64_t, int>& oldConditionTrackerMap,
+                                    const vector<sp<ConditionTracker>>& oldConditionTrackers,
+                                    const unordered_map<int64_t, int>& newConditionTrackerMap,
+                                    const set<int64_t>& replacedMatchers,
+                                    vector<UpdateStatus>& conditionsToUpdate,
+                                    vector<bool>& cycleTracker) {
+    // Have already examined this condition.
+    if (conditionsToUpdate[conditionIdx] != UPDATE_UNKNOWN) {
+        return true;
+    }
+
+    const Predicate& predicate = config.predicate(conditionIdx);
+    int64_t id = predicate.id();
+    // Check if new condition.
+    const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id);
+    if (oldConditionTrackerIt == oldConditionTrackerMap.end()) {
+        conditionsToUpdate[conditionIdx] = UPDATE_NEW;
+        return true;
+    }
+
+    // This is an existing condition. Check if it has changed.
+    string serializedCondition;
+    if (!predicate.SerializeToString(&serializedCondition)) {
+        ALOGE("Unable to serialize matcher %lld", (long long)id);
+        return false;
+    }
+    uint64_t newProtoHash = Hash64(serializedCondition);
+    if (newProtoHash != oldConditionTrackers[oldConditionTrackerIt->second]->getProtoHash()) {
+        conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+        return true;
+    }
+
+    switch (predicate.contents_case()) {
+        case Predicate::ContentsCase::kSimplePredicate: {
+            // Need to check if any of the underlying matchers changed.
+            const SimplePredicate& simplePredicate = predicate.simple_predicate();
+            if (simplePredicate.has_start()) {
+                if (replacedMatchers.find(simplePredicate.start()) != replacedMatchers.end()) {
+                    conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+                    return true;
+                }
+            }
+            if (simplePredicate.has_stop()) {
+                if (replacedMatchers.find(simplePredicate.stop()) != replacedMatchers.end()) {
+                    conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+                    return true;
+                }
+            }
+            if (simplePredicate.has_stop_all()) {
+                if (replacedMatchers.find(simplePredicate.stop_all()) != replacedMatchers.end()) {
+                    conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+                    return true;
+                }
+            }
+            conditionsToUpdate[conditionIdx] = UPDATE_PRESERVE;
+            return true;
+        }
+        case Predicate::ContentsCase::kCombination: {
+            // Need to recurse on the children to see if any of the child predicates changed.
+            cycleTracker[conditionIdx] = true;
+            UpdateStatus status = UPDATE_PRESERVE;
+            for (const int64_t childPredicateId : predicate.combination().predicate()) {
+                const auto& childIt = newConditionTrackerMap.find(childPredicateId);
+                if (childIt == newConditionTrackerMap.end()) {
+                    ALOGW("Predicate %lld not found in the config", (long long)childPredicateId);
+                    return false;
+                }
+                const int childIdx = childIt->second;
+                if (cycleTracker[childIdx]) {
+                    ALOGE("Cycle detected in predicate config");
+                    return false;
+                }
+                if (!determineConditionUpdateStatus(config, childIdx, oldConditionTrackerMap,
+                                                    oldConditionTrackers, newConditionTrackerMap,
+                                                    replacedMatchers, conditionsToUpdate,
+                                                    cycleTracker)) {
+                    return false;
+                }
+
+                if (conditionsToUpdate[childIdx] == UPDATE_REPLACE) {
+                    status = UPDATE_REPLACE;
+                    break;
+                }
+            }
+            conditionsToUpdate[conditionIdx] = status;
+            cycleTracker[conditionIdx] = false;
+            return true;
+        }
+        default: {
+            ALOGE("Predicate \"%lld\" malformed", (long long)id);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool updateConditions(const ConfigKey& key, const StatsdConfig& config,
+                      const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                      const set<int64_t>& replacedMatchers,
+                      const unordered_map<int64_t, int>& oldConditionTrackerMap,
+                      const vector<sp<ConditionTracker>>& oldConditionTrackers,
+                      unordered_map<int64_t, int>& newConditionTrackerMap,
+                      vector<sp<ConditionTracker>>& newConditionTrackers,
+                      unordered_map<int, vector<int>>& trackerToConditionMap,
+                      vector<ConditionState>& conditionCache, set<int64_t>& replacedConditions) {
+    vector<Predicate> conditionProtos;
+    const int conditionTrackerCount = config.predicate_size();
+    conditionProtos.reserve(conditionTrackerCount);
+    newConditionTrackers.reserve(conditionTrackerCount);
+    conditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated);
+
+    for (int i = 0; i < conditionTrackerCount; i++) {
+        const Predicate& condition = config.predicate(i);
+        if (newConditionTrackerMap.find(condition.id()) != newConditionTrackerMap.end()) {
+            ALOGE("Duplicate Predicate found!");
+            return false;
+        }
+        newConditionTrackerMap[condition.id()] = i;
+        conditionProtos.push_back(condition);
+    }
+
+    vector<UpdateStatus> conditionsToUpdate(conditionTrackerCount, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(conditionTrackerCount, false);
+    for (int i = 0; i < conditionTrackerCount; i++) {
+        if (!determineConditionUpdateStatus(config, i, oldConditionTrackerMap, oldConditionTrackers,
+                                            newConditionTrackerMap, replacedMatchers,
+                                            conditionsToUpdate, cycleTracker)) {
+            return false;
+        }
+    }
+
+    // Update status has been determined for all conditions. Now perform the update.
+    set<int> preservedConditions;
+    for (int i = 0; i < conditionTrackerCount; i++) {
+        const Predicate& predicate = config.predicate(i);
+        const int64_t id = predicate.id();
+        switch (conditionsToUpdate[i]) {
+            case UPDATE_PRESERVE: {
+                preservedConditions.insert(i);
+                const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id);
+                if (oldConditionTrackerIt == oldConditionTrackerMap.end()) {
+                    ALOGE("Could not find Predicate %lld in the previous config, but expected it "
+                          "to be there",
+                          (long long)id);
+                    return false;
+                }
+                const int oldIndex = oldConditionTrackerIt->second;
+                newConditionTrackers.push_back(oldConditionTrackers[oldIndex]);
+                break;
+            }
+            case UPDATE_REPLACE:
+                replacedConditions.insert(id);
+                [[fallthrough]];  // Intentionally fallthrough to create the new condition tracker.
+            case UPDATE_NEW: {
+                sp<ConditionTracker> tracker =
+                        createConditionTracker(key, predicate, i, atomMatchingTrackerMap);
+                if (tracker == nullptr) {
+                    return false;
+                }
+                newConditionTrackers.push_back(tracker);
+                break;
+            }
+            default: {
+                ALOGE("Condition \"%lld\" update state is unknown. This should never happen",
+                      (long long)id);
+                return false;
+            }
+        }
+    }
+
+    // Update indices of preserved predicates.
+    for (const int conditionIndex : preservedConditions) {
+        if (!newConditionTrackers[conditionIndex]->onConfigUpdated(
+                    conditionProtos, conditionIndex, newConditionTrackers, atomMatchingTrackerMap,
+                    newConditionTrackerMap)) {
+            ALOGE("Failed to update condition %lld",
+                  (long long)newConditionTrackers[conditionIndex]->getConditionId());
+            return false;
+        }
+    }
+
+    std::fill(cycleTracker.begin(), cycleTracker.end(), false);
+    for (int conditionIndex = 0; conditionIndex < conditionTrackerCount; conditionIndex++) {
+        const sp<ConditionTracker>& conditionTracker = newConditionTrackers[conditionIndex];
+        // Calling init on preserved conditions is OK. It is needed to fill the condition cache.
+        if (!conditionTracker->init(conditionProtos, newConditionTrackers, newConditionTrackerMap,
+                                    cycleTracker, conditionCache)) {
+            return false;
+        }
+        for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) {
+            vector<int>& conditionList = trackerToConditionMap[trackerIndex];
+            conditionList.push_back(conditionIndex);
+        }
+    }
+    return true;
+}
+
 bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
                         const sp<StatsPullerManager>& pullerManager,
                         const sp<AlarmMonitor>& anomalyAlarmMonitor,
@@ -189,14 +401,34 @@
                         const int64_t currentTimeNs,
                         const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
                         const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                        const vector<sp<ConditionTracker>>& oldConditionTrackers,
+                        const unordered_map<int64_t, int>& oldConditionTrackerMap,
                         set<int>& allTagIds,
                         vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
-                        unordered_map<int64_t, int>& newAtomMatchingTrackerMap) {
-    if (!updateAtomTrackers(config, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers,
-                            allTagIds, newAtomMatchingTrackerMap, newAtomMatchingTrackers)) {
+                        unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+                        vector<sp<ConditionTracker>>& newConditionTrackers,
+                        unordered_map<int64_t, int>& newConditionTrackerMap,
+                        unordered_map<int, vector<int>>& trackerToConditionMap) {
+    set<int64_t> replacedMatchers;
+    set<int64_t> replacedConditions;
+    vector<ConditionState> conditionCache;
+
+    if (!updateAtomMatchingTrackers(config, uidMap, oldAtomMatchingTrackerMap,
+                                    oldAtomMatchingTrackers, allTagIds, newAtomMatchingTrackerMap,
+                                    newAtomMatchingTrackers, replacedMatchers)) {
         ALOGE("updateAtomMatchingTrackers failed");
         return false;
     }
+    VLOG("updateAtomMatchingTrackers succeeded");
+
+    if (!updateConditions(key, config, newAtomMatchingTrackerMap, replacedMatchers,
+                          oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap,
+                          newConditionTrackers, trackerToConditionMap, conditionCache,
+                          replacedConditions)) {
+        ALOGE("updateConditions failed");
+        return false;
+    }
+    VLOG("updateConditions succeeded");
 
     return true;
 }
diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
index ae7b216..7ba684a 100644
--- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
+++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.h
@@ -19,6 +19,7 @@
 #include <vector>
 
 #include "anomaly/AlarmMonitor.h"
+#include "condition/ConditionTracker.h"
 #include "external/StatsPullerManager.h"
 #include "matchers/AtomMatchingTracker.h"
 
@@ -31,30 +32,33 @@
 // All other functions are intermediate steps, created to make unit testing easier.
 
 // Possible update states for a component. PRESERVE means we should keep the existing one.
-// REPLACE means we should create a new one, either because it didn't exist or it changed.
+// REPLACE means we should create a new one because the existing one changed
+// NEW means we should create a new one because one does not currently exist.
 enum UpdateStatus {
     UPDATE_UNKNOWN = 0,
     UPDATE_PRESERVE = 1,
     UPDATE_REPLACE = 2,
+    UPDATE_NEW = 3,
 };
 
 // Recursive function to determine if a matcher needs to be updated.
 // input:
 // [config]: the input StatsdConfig
 // [matcherIdx]: the index of the current matcher to be updated
-// [newAtomMatchingTrackerMap]: matcher id to index mapping in the input StatsdConfig
 // [oldAtomMatchingTrackerMap]: matcher id to index mapping in the existing MetricsManager
 // [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers
+// [newAtomMatchingTrackerMap]: matcher id to index mapping in the input StatsdConfig
 // output:
 // [matchersToUpdate]: vector of the update status of each matcher. The matcherIdx index will
 //                     be updated from UPDATE_UNKNOWN after this call.
 // [cycleTracker]: intermediate param used during recursion.
-bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherIdx,
-                                  const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
-                                  const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
-                                  const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
-                                  vector<UpdateStatus>& matchersToUpdate,
-                                  vector<bool>& cycleTracker);
+// Returns whether the function was successful or not.
+bool determineMatcherUpdateStatus(
+        const StatsdConfig& config, const int matcherIdx,
+        const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        std::vector<UpdateStatus>& matchersToUpdate, std::vector<bool>& cycleTracker);
 
 // Updates the AtomMatchingTrackers.
 // input:
@@ -64,12 +68,61 @@
 // output:
 // [allTagIds]: contains the set of all interesting tag ids to this config.
 // [newAtomMatchingTrackerMap]: new matcher id to index mapping
-// [newAtomMatchers]: stores the new AtomMatchingTrackers
-bool updateAtomTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
-                        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
-                        const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
-                        set<int>& allTagIds, unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
-                        vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers);
+// [newAtomMatchingTrackers]: stores the new AtomMatchingTrackers
+// [replacedMatchers]: set of matcher ids that changed and have been replaced
+bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
+                                const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                                const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+                                std::set<int>& allTagIds,
+                                std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+                                std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
+                                std::set<int64_t>& replacedMatchers);
+
+// Recursive function to determine if a condition needs to be updated.
+// input:
+// [config]: the input StatsdConfig
+// [conditionIdx]: the index of the current condition to be updated
+// [oldConditionTrackerMap]: condition id to index mapping in the existing MetricsManager
+// [oldConditionTrackers]: stores the existing ConditionTrackers
+// [newConditionTrackerMap]: condition id to index mapping in the input StatsdConfig
+// [replacedMatchers]: set of replaced matcher ids. conditions using these matchers must be replaced
+// output:
+// [conditionsToUpdate]: vector of the update status of each condition. The conditionIdx index will
+//                       be updated from UPDATE_UNKNOWN after this call.
+// [cycleTracker]: intermediate param used during recursion.
+// Returns whether the function was successful or not.
+bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx,
+                                    const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
+                                    const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
+                                    const std::unordered_map<int64_t, int>& newConditionTrackerMap,
+                                    const std::set<int64_t>& replacedMatchers,
+                                    std::vector<UpdateStatus>& conditionsToUpdate,
+                                    std::vector<bool>& cycleTracker);
+
+// Updates ConditionTrackers
+// input:
+// [config]: the input config
+// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
+// [replacedMatchers]: ids of replaced matchers. conditions depending on these must also be replaced
+// [oldConditionTrackerMap]: existing matcher id to index mapping
+// [oldConditionTrackers]: stores the existing ConditionTrackers
+// output:
+// [newConditionTrackerMap]: new condition id to index mapping
+// [newConditionTrackers]: stores the sp to all the ConditionTrackers
+// [trackerToConditionMap]: contains the mapping from the index of an atom matcher
+//                          to indices of condition trackers that use the matcher
+// [conditionCache]: stores the current conditions for each ConditionTracker
+// [replacedConditions]: set of matcher ids that have changed and have been replaced
+bool updateConditions(const ConfigKey& key, const StatsdConfig& config,
+                      const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                      const std::set<int64_t>& replacedMatchers,
+                      const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
+                      const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
+                      std::unordered_map<int64_t, int>& newConditionTrackerMap,
+                      std::vector<sp<ConditionTracker>>& newConditionTrackers,
+                      std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
+                      std::vector<ConditionState>& conditionCache,
+                      std::set<int64_t>& replacedConditions);
 
 // Updates the existing MetricsManager from a new StatsdConfig.
 // Parameters are the members of MetricsManager. See MetricsManager for declaration.
@@ -79,10 +132,15 @@
                         const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
                         const int64_t currentTimeNs,
                         const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
-                        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                        const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                        const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
+                        const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
                         std::set<int>& allTagIds,
                         std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
-                        unordered_map<int64_t, int>& newAtomMatchingTrackerMap);
+                        std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+                        std::vector<sp<ConditionTracker>>& newConditionTrackers,
+                        std::unordered_map<int64_t, int>& newConditionTrackerMap,
+                        std::unordered_map<int, std::vector<int>>& trackerToConditionMap);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
index e40fbdb..2e3e434 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -74,16 +74,37 @@
         case AtomMatcher::ContentsCase::kSimpleAtomMatcher:
             return new SimpleAtomMatchingTracker(logMatcher.id(), index, protoHash,
                                                  logMatcher.simple_atom_matcher(), uidMap);
-            break;
         case AtomMatcher::ContentsCase::kCombination:
             return new CombinationAtomMatchingTracker(logMatcher.id(), index, protoHash);
-            break;
         default:
             ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id());
             return nullptr;
     }
 }
 
+sp<ConditionTracker> createConditionTracker(
+        const ConfigKey& key, const Predicate& predicate, const int index,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+    string serializedPredicate;
+    if (!predicate.SerializeToString(&serializedPredicate)) {
+        ALOGE("Unable to serialize predicate %lld", (long long)predicate.id());
+        return nullptr;
+    }
+    uint64_t protoHash = Hash64(serializedPredicate);
+    switch (predicate.contents_case()) {
+        case Predicate::ContentsCase::kSimplePredicate: {
+            return new SimpleConditionTracker(key, predicate.id(), protoHash, index,
+                                              predicate.simple_predicate(), atomMatchingTrackerMap);
+        }
+        case Predicate::ContentsCase::kCombination: {
+            return new CombinationConditionTracker(predicate.id(), index, protoHash);
+        }
+        default:
+            ALOGE("Predicate \"%lld\" malformed", (long long)predicate.id());
+            return nullptr;
+    }
+}
+
 bool handleMetricWithAtomMatchingTrackers(
         const int64_t what, const int metricIndex, const bool usedForDimension,
         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
@@ -266,8 +287,7 @@
 
     for (int i = 0; i < atomMatcherCount; i++) {
         const AtomMatcher& logMatcher = config.atom_matcher(i);
-        int index = allAtomMatchingTrackers.size();
-        sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(logMatcher, index, uidMap);
+        sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(logMatcher, i, uidMap);
         if (tracker == nullptr) {
             return false;
         }
@@ -276,7 +296,7 @@
             ALOGE("Duplicate AtomMatcher found!");
             return false;
         }
-        atomMatchingTrackerMap[logMatcher.id()] = index;
+        atomMatchingTrackerMap[logMatcher.id()] = i;
         matcherConfigs.push_back(logMatcher);
     }
 
@@ -307,28 +327,17 @@
 
     for (int i = 0; i < conditionTrackerCount; i++) {
         const Predicate& condition = config.predicate(i);
-        int index = allConditionTrackers.size();
-        switch (condition.contents_case()) {
-            case Predicate::ContentsCase::kSimplePredicate: {
-                allConditionTrackers.push_back(new SimpleConditionTracker(
-                        key, condition.id(), index, condition.simple_predicate(),
-                        atomMatchingTrackerMap));
-                break;
-            }
-            case Predicate::ContentsCase::kCombination: {
-                allConditionTrackers.push_back(
-                        new CombinationConditionTracker(condition.id(), index));
-                break;
-            }
-            default:
-                ALOGE("Predicate \"%lld\" malformed", (long long)condition.id());
-                return false;
+        sp<ConditionTracker> tracker =
+                createConditionTracker(key, condition, i, atomMatchingTrackerMap);
+        if (tracker == nullptr) {
+            return false;
         }
+        allConditionTrackers.push_back(tracker);
         if (conditionTrackerMap.find(condition.id()) != conditionTrackerMap.end()) {
             ALOGE("Duplicate Predicate found!");
             return false;
         }
-        conditionTrackerMap[condition.id()] = index;
+        conditionTrackerMap[condition.id()] = i;
         conditionConfigs.push_back(condition);
     }
 
@@ -934,6 +943,7 @@
                       vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
                       unordered_map<int64_t, int>& atomMatchingTrackerMap,
                       vector<sp<ConditionTracker>>& allConditionTrackers,
+                      unordered_map<int64_t, int>& conditionTrackerMap,
                       vector<sp<MetricProducer>>& allMetricProducers,
                       vector<sp<AnomalyTracker>>& allAnomalyTrackers,
                       vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
@@ -944,7 +954,6 @@
                       unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
                       unordered_map<int64_t, int>& alertTrackerMap,
                       vector<int>& metricsWithActivation, std::set<int64_t>& noReportMetricIds) {
-    unordered_map<int64_t, int> conditionTrackerMap;
     vector<ConditionState> initialConditionCache;
     unordered_map<int64_t, int> metricProducerMap;
     unordered_map<int64_t, int> stateAtomIdMap;
diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
index 4cfd1b0..6eabcf4 100644
--- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h
@@ -42,6 +42,17 @@
 sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index,
                                                   const sp<UidMap>& uidMap);
 
+// Create a ConditionTracker.
+// input:
+// [predicate]: the input Predicate from the StatsdConfig
+// [index]: the index of the condition tracker
+// [atomMatchingTrackerMap]: map of atom matcher id to its index in allAtomMatchingTrackers
+// output:
+// new ConditionTracker, or null if the tracker is unable to be created
+sp<ConditionTracker> createConditionTracker(
+        const ConfigKey& key, const Predicate& predicate, const int index,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap);
+
 // Helper functions for MetricsManager to initialize from StatsdConfig.
 // *Note*: only initStatsdConfig() should be called from outside.
 // All other functions are intermediate
@@ -77,7 +88,6 @@
                     std::unordered_map<int64_t, int>& conditionTrackerMap,
                     std::vector<sp<ConditionTracker>>& allConditionTrackers,
                     std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
-                    std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
                     std::vector<ConditionState>& initialConditionCache);
 
 // Initialize State maps using State protos in the config. These maps will
@@ -111,7 +121,6 @@
         const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
         const std::unordered_map<int64_t, int>& conditionTrackerMap,
-        const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
         const unordered_map<int64_t, int>& stateAtomIdMap,
         const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
@@ -135,6 +144,7 @@
                       std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
                       std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
                       std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                      std::unordered_map<int64_t, int>& conditionTrackerMap,
                       std::vector<sp<MetricProducer>>& allMetricProducers,
                       vector<sp<AnomalyTracker>>& allAnomalyTrackers,
                       vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 07b5311b..8998b5f 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -39,6 +39,7 @@
 const int ATTRIBUTION_NODE_FIELD_ID = 1;
 const int ATTRIBUTION_UID_FIELD_ID = 1;
 const int TAG_ID = 1;
+const uint64_t protoHash = 0x123456789;
 
 SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
                                          bool outputSlicedUid, Position position) {
@@ -123,7 +124,7 @@
     trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
     trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"),
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
                                             0 /*tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
 
@@ -177,7 +178,7 @@
     trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
     trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"),
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
                                             0 /*tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
 
@@ -231,8 +232,9 @@
     trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
     trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), 0 /*tracker index*/,
-                                            simplePredicate, trackerNameIndexMap);
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
+                                            0 /*tracker index*/, simplePredicate,
+                                            trackerNameIndexMap);
     EXPECT_FALSE(conditionTracker.isSliced());
 
     // This event is not accessed in this test besides dimensions which is why this is okay.
@@ -317,7 +319,7 @@
     trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
     trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"),
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
                                             0 /*condition tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
     EXPECT_FALSE(conditionTracker.isSliced());
@@ -392,7 +394,7 @@
         trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
         trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
 
-        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
                                                 0 /*condition tracker index*/, simplePredicate,
                                                 trackerNameIndexMap);
 
@@ -514,7 +516,7 @@
     trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
     trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
                                             0 /*condition tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
 
@@ -610,7 +612,7 @@
         trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
         trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
 
-        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
                                                 0 /*condition tracker index*/, simplePredicate,
                                                 trackerNameIndexMap);
 
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 1000aea..8790fe4 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -57,7 +57,7 @@
 double epsilon = 0.001;
 
 static void assertPastBucketValuesSingleKey(
-        const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets,
+        const std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>>& mPastBuckets,
         const std::initializer_list<int>& expectedValuesList,
         const std::initializer_list<int64_t>& expectedDurationNsList,
         const std::initializer_list<int64_t>& expectedStartTimeNsList,
@@ -79,7 +79,7 @@
     ASSERT_EQ(1, mPastBuckets.size());
     ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size());
 
-    const vector<ValueBucket>& buckets = mPastBuckets.begin()->second;
+    const vector<PastValueBucket>& buckets = mPastBuckets.begin()->second;
     for (int i = 0; i < expectedValues.size(); i++) {
         EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value)
                 << "Values differ at index " << i;
@@ -288,8 +288,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -304,8 +305,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(23, curBaseInfo.base.long_value);
@@ -322,8 +323,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(36, curBaseInfo.base.long_value);
@@ -426,8 +427,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -455,8 +457,8 @@
     allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     // the base was reset
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -489,8 +491,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -502,8 +505,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -516,8 +519,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(36, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -549,8 +552,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -562,8 +566,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -573,8 +577,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(36, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -624,8 +628,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // startUpdated:false sum:0 start:100
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -641,8 +646,8 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(110, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -654,8 +659,8 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(20, curInterval.value.long_value);
     EXPECT_EQ(false, curBaseInfo.hasBase);
@@ -879,8 +884,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -888,7 +894,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(30, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -925,8 +931,8 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(20, curInterval.value.long_value);
 
     LogEvent event3(/*uid=*/0, /*pid=*/0);
@@ -935,7 +941,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(50, curInterval.value.long_value);
 
     valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35);
@@ -946,7 +952,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(50, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1089,8 +1095,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     // startUpdated:true sum:0 start:11
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -1104,8 +1111,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // tartUpdated:false sum:12
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(23, curBaseInfo.base.long_value);
@@ -1121,8 +1128,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket6StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // startUpdated:false sum:12
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(36, curBaseInfo.base.long_value);
@@ -1180,8 +1187,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1189,8 +1197,8 @@
 
     // pull on bucket boundary come late, condition change happens before it
     valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
     EXPECT_EQ(false, curBaseInfo.hasBase);
@@ -1203,8 +1211,8 @@
 
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 }
@@ -1252,8 +1260,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // startUpdated:false sum:0 start:100
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -1265,8 +1274,8 @@
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 
@@ -1274,8 +1283,8 @@
     valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(130, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1286,8 +1295,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 50, 140));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50);
 
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(140, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1327,7 +1336,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1335,7 +1344,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1364,7 +1373,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1374,7 +1383,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(20, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1405,7 +1414,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval;
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(1, curInterval.sampleSize);
@@ -1414,7 +1423,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(25, curInterval.value.long_value);
     EXPECT_EQ(2, curInterval.sampleSize);
 
@@ -1449,7 +1458,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1457,7 +1466,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(25, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1487,8 +1496,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1499,7 +1509,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(5, curInterval.value.long_value);
 
@@ -1509,8 +1519,8 @@
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
 
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1520,8 +1530,8 @@
     CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15);
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1558,12 +1568,13 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(20, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1572,12 +1583,12 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(5, curInterval.value.long_value);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(2, curInterval.value.long_value);
 
@@ -1587,14 +1598,14 @@
 
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(25, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1604,13 +1615,13 @@
 
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(29, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1656,9 +1667,9 @@
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     auto iter = valueProducer->mCurrentSlicedBucket.begin();
-    auto& interval1 = iter->second[0];
+    auto& interval1 = iter->second.intervals[0];
     auto iterBase = valueProducer->mCurrentBaseInfo.begin();
-    auto& baseInfo1 = iterBase->second[0];
+    auto& baseInfo1 = iterBase->second.baseInfos[0];
     EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo1.hasBase);
     EXPECT_EQ(3, baseInfo1.base.long_value);
@@ -1692,8 +1703,8 @@
     }
     EXPECT_TRUE(it != iter);
     EXPECT_TRUE(itBase != iterBase);
-    auto& interval2 = it->second[0];
-    auto& baseInfo2 = itBase->second[0];
+    auto& interval2 = it->second.intervals[0];
+    auto& baseInfo2 = itBase->second.baseInfos[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(4, baseInfo2.base.long_value);
@@ -1732,9 +1743,10 @@
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     const auto& it = valueProducer->mCurrentSlicedBucket.begin();
-    ValueMetricProducer::Interval& interval1 = it->second[0];
+    ValueMetricProducer::Interval& interval1 = it->second.intervals[0];
     ValueMetricProducer::BaseInfo& baseInfo1 =
-            valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
+            valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())
+                    ->second.baseInfos[0];
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo1.hasBase);
     EXPECT_EQ(3, baseInfo1.base.long_value);
@@ -1761,9 +1773,10 @@
         }
     }
     EXPECT_TRUE(it2 != it);
-    ValueMetricProducer::Interval& interval2 = it2->second[0];
+    ValueMetricProducer::Interval& interval2 = it2->second.intervals[0];
     ValueMetricProducer::BaseInfo& baseInfo2 =
-            valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
+            valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())
+                    ->second.baseInfos[0];
     EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(4, baseInfo2.base.long_value);
@@ -1792,14 +1805,16 @@
     // Get new references now that entries have been deleted from the map
     const auto& it3 = valueProducer->mCurrentSlicedBucket.begin();
     const auto& it4 = std::next(valueProducer->mCurrentSlicedBucket.begin());
-    ASSERT_EQ(it3->second.size(), 1);
-    ASSERT_EQ(it4->second.size(), 1);
-    ValueMetricProducer::Interval& interval3 = it3->second[0];
-    ValueMetricProducer::Interval& interval4 = it4->second[0];
+    ASSERT_EQ(it3->second.intervals.size(), 1);
+    ASSERT_EQ(it4->second.intervals.size(), 1);
+    ValueMetricProducer::Interval& interval3 = it3->second.intervals[0];
+    ValueMetricProducer::Interval& interval4 = it4->second.intervals[0];
     ValueMetricProducer::BaseInfo& baseInfo3 =
-            valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())->second[0];
+            valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())
+                    ->second.baseInfos[0];
     ValueMetricProducer::BaseInfo& baseInfo4 =
-            valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())->second[0];
+            valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())
+                    ->second.baseInfos[0];
 
     EXPECT_EQ(true, baseInfo3.hasBase);
     EXPECT_EQ(5, baseInfo3.base.long_value);
@@ -1837,9 +1852,9 @@
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     auto iter = valueProducer->mCurrentSlicedBucket.begin();
-    auto& interval1 = iter->second[0];
+    auto& interval1 = iter->second.intervals[0];
     auto iterBase = valueProducer->mCurrentBaseInfo.begin();
-    auto& baseInfo1 = iterBase->second[0];
+    auto& baseInfo1 = iterBase->second.baseInfos[0];
     EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo1.hasBase);
     EXPECT_EQ(3, baseInfo1.base.long_value);
@@ -1875,8 +1890,8 @@
     }
     EXPECT_TRUE(it != iter);
     EXPECT_TRUE(itBase != iterBase);
-    auto interval2 = it->second[0];
-    auto baseInfo2 = itBase->second[0];
+    auto interval2 = it->second.intervals[0];
+    auto baseInfo2 = itBase->second.baseInfos[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(4, baseInfo2.base.long_value);
@@ -1889,8 +1904,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     // Only one interval left. One was trimmed.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(5, baseInfo2.base.long_value);
@@ -1903,8 +1918,8 @@
     allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
 
-    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(14, baseInfo2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
@@ -1943,8 +1958,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo& curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1980,8 +1996,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo& curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2030,8 +2047,9 @@
     valueProducer->onConditionChanged(false, bucketStartTimeNs + 1);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(false, valueProducer->mHasGlobalBase);
@@ -2103,8 +2121,9 @@
     valueProducer->mHasGlobalBase = true;
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2156,8 +2175,9 @@
     // Contains base from last pull which was successful.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(140, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2294,8 +2314,9 @@
     // Contains base from last pull which was successful.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(140, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2373,8 +2394,9 @@
     // Last pull failed so base has been reset.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(false, valueProducer->mHasGlobalBase);
@@ -2460,8 +2482,9 @@
     valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2469,8 +2492,8 @@
     // Empty pull.
     valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(false, valueProducer->mHasGlobalBase);
@@ -2513,8 +2536,9 @@
     valueProducer->onConditionChanged(true, bucketStartTimeNs + 12);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2524,8 +2548,8 @@
     allData.clear();
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // Data is empty, base should be reset.
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(5, curBaseInfo.base.long_value);
@@ -2570,14 +2594,14 @@
     ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
     auto iterator = valueProducer->mCurrentSlicedBucket.begin();
     auto baseInfoIter = valueProducer->mCurrentBaseInfo.begin();
-    EXPECT_EQ(true, baseInfoIter->second[0].hasBase);
-    EXPECT_EQ(2, baseInfoIter->second[0].base.long_value);
-    EXPECT_EQ(false, iterator->second[0].hasValue);
+    EXPECT_EQ(true, baseInfoIter->second.baseInfos[0].hasBase);
+    EXPECT_EQ(2, baseInfoIter->second.baseInfos[0].base.long_value);
+    EXPECT_EQ(false, iterator->second.intervals[0].hasValue);
     iterator++;
     baseInfoIter++;
-    EXPECT_EQ(false, baseInfoIter->second[0].hasBase);
-    EXPECT_EQ(1, baseInfoIter->second[0].base.long_value);
-    EXPECT_EQ(false, iterator->second[0].hasValue);
+    EXPECT_EQ(false, baseInfoIter->second.baseInfos[0].hasBase);
+    EXPECT_EQ(1, baseInfoIter->second.baseInfos[0].base.long_value);
+    EXPECT_EQ(false, iterator->second.intervals[0].hasValue);
 
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 }
@@ -2676,8 +2700,8 @@
 
     valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(5, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2811,8 +2835,8 @@
     valueProducer->onConditionChanged(false, bucketStartTimeNs + 12);
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(2, curInterval.value.long_value);
 
@@ -3045,8 +3069,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(20, curInterval.value.long_value);
@@ -3058,8 +3083,8 @@
 
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 }
@@ -3091,8 +3116,9 @@
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 }
@@ -3984,18 +4010,18 @@
     // Base for dimension key {}
     auto it = valueProducer->mCurrentSlicedBucket.begin();
     auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(3, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, kStateUnknown}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after screen state change kStateUnknown->ON.
     auto screenEvent = CreateScreenStateChangedEvent(
@@ -4005,19 +4031,19 @@
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, kStateUnknown}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change ON->OFF.
     screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
@@ -4027,26 +4053,26 @@
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(9, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, ON}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
     // Value for dimension, state key {{}, kStateUnknown}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change OFF->ON.
     screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
@@ -4056,35 +4082,35 @@
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(21, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(21, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, OFF}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(12, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(12, it->second.intervals[0].value.long_value);
     // Value for dimension, state key {{}, ON}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
     // Value for dimension, state key {{}, kStateUnknown}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Start dump report and check output.
     ProtoOutputStream output;
@@ -4195,18 +4221,18 @@
     // Base for dimension key {}
     auto it = valueProducer->mCurrentSlicedBucket.begin();
     auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(3, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, {kStateUnknown}}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after screen state change kStateUnknown->ON.
     auto screenEvent = CreateScreenStateChangedEvent(
@@ -4216,19 +4242,19 @@
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(screenOnGroup.group_id(),
-              itBase->second[0].currentState.getValues()[0].mValue.long_value);
+              itBase->second.currentState.getValues()[0].mValue.long_value);
     // Value for dimension, state key {{}, kStateUnknown}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change ON->VR.
     // Both ON and VR are in the same state group, so the base should not change.
@@ -4239,19 +4265,19 @@
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(screenOnGroup.group_id(),
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, kStateUnknown}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change VR->ON.
     // Both ON and VR are in the same state group, so the base should not change.
@@ -4262,19 +4288,19 @@
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(screenOnGroup.group_id(),
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, kStateUnknown}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Bucket status after screen state change VR->OFF.
     screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
@@ -4284,27 +4310,27 @@
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(21, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(21, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(screenOffGroup.group_id(),
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, ON GROUP}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(screenOnGroup.group_id(),
               it->first.getStateValuesKey().getValues()[0].mValue.long_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(16, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(16, it->second.intervals[0].value.long_value);
     // Value for dimension, state key {{}, kStateUnknown}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Start dump report and check output.
     ProtoOutputStream output;
@@ -4447,35 +4473,35 @@
     // Base for dimension key {uid 1}.
     auto it = valueProducer->mCurrentSlicedBucket.begin();
     auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(3, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{uid 1}, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
     // Base for dimension key {uid 2}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(7, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(7, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{uid 2}, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
     auto uidProcessEvent = CreateUidProcessStateChangedEvent(
@@ -4485,36 +4511,36 @@
     // Base for dimension key {uid 1}.
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(6, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 1, kStateUnknown}.
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
 
     // Base for dimension key {uid 2}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(7, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(7, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 2, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after uid 2 process state change kStateUnknown -> Background.
     uidProcessEvent = CreateUidProcessStateChangedEvent(
@@ -4524,36 +4550,36 @@
     // Base for dimension key {uid 1}.
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(6, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 1, kStateUnknown}.
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
 
     // Base for dimension key {uid 2}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(9, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 2, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Pull at end of first bucket.
     vector<shared_ptr<LogEvent>> allData;
@@ -4570,36 +4596,36 @@
     it = valueProducer->mCurrentSlicedBucket.begin();
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(15, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 2, BACKGROUND}.
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Base for dimension key {uid 1}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(10, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(10, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 1, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Value for key {uid 1, FOREGROUND}
     it++;
@@ -4608,7 +4634,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Value for key {uid 2, kStateUnknown}
     it++;
@@ -4617,7 +4643,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after uid 1 process state change from Foreground -> Background.
     uidProcessEvent = CreateUidProcessStateChangedEvent(
@@ -4630,35 +4656,35 @@
     // Base for dimension key {uid 2}.
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(15, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 2, BACKGROUND}.
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
     // Base for dimension key {uid 1}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(13, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(13, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 1, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
     // Value for key {uid 1, FOREGROUND}
     it++;
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4666,8 +4692,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
     // Value for key {uid 2, kStateUnknown}
     it++;
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
@@ -4675,7 +4701,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after uid 1 process state change Background->Foreground.
     uidProcessEvent = CreateUidProcessStateChangedEvent(
@@ -4687,36 +4713,36 @@
     // Base for dimension key {uid 2}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(15, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 2, BACKGROUND}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Base for dimension key {uid 1}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(17, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(17, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 1, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Value for key {uid 1, BACKGROUND}
     it++;
@@ -4725,8 +4751,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
 
     // Value for key {uid 1, FOREGROUND}
     it++;
@@ -4735,8 +4761,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
 
     // Value for key {uid 2, kStateUnknown}
     it++;
@@ -4857,23 +4883,23 @@
     valueProducer->onConditionChanged(true, bucketStartTimeNs + 20 * NS_PER_SEC);
     // Base for dimension key {}
     ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
-    std::unordered_map<HashableDimensionKey, std::vector<ValueMetricProducer::BaseInfo>>::iterator
+    std::unordered_map<HashableDimensionKey, ValueMetricProducer::DimensionsInWhatInfo>::iterator
             itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(3, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::ON,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {{}, -1}
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    std::unordered_map<MetricDimensionKey, std::vector<ValueMetricProducer::Interval>>::iterator
-            it = valueProducer->mCurrentSlicedBucket.begin();
+    std::unordered_map<MetricDimensionKey, ValueMetricProducer::CurrentValueBucket>::iterator it =
+            valueProducer->mCurrentSlicedBucket.begin();
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
 
     // Bucket status after battery saver mode OFF event.
     unique_ptr<LogEvent> batterySaverOffEvent =
@@ -4882,12 +4908,12 @@
     // Base for dimension key {}
     ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
     itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::OFF,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {{}, ON}
     ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
     it = valueProducer->mCurrentSlicedBucket.begin();
@@ -4895,8 +4921,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::ON,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
 
     // Pull at end of first bucket.
     vector<shared_ptr<LogEvent>> allData;
@@ -4909,23 +4935,23 @@
     // Base for dimension key {}
     ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
     itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(11, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(11, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::OFF,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
 
     // Bucket 2 status after condition change to false.
     valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC);
     // Base for dimension key {}
     ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
     itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
-    EXPECT_FALSE(itBase->second[0].hasBase);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_FALSE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::OFF,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {{}, OFF}
     ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
     it = valueProducer->mCurrentSlicedBucket.begin();
@@ -4933,8 +4959,8 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::OFF,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
 
     // Start dump report and check output.
     ProtoOutputStream output;
diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
index f6d3061..890884b 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -14,6 +14,7 @@
 
 #include "src/metrics/parsing_utils/config_update_utils.h"
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <private/android_filesystem_config.h>
 #include <stdio.h>
@@ -23,6 +24,9 @@
 #include <vector>
 
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "src/condition/CombinationConditionTracker.h"
+#include "src/condition/SimpleConditionTracker.h"
+#include "src/matchers/CombinationAtomMatchingTracker.h"
 #include "src/metrics/parsing_utils/metrics_manager_util.h"
 #include "tests/statsd_test_util.h"
 
@@ -52,6 +56,7 @@
 vector<sp<AtomMatchingTracker>> oldAtomMatchingTrackers;
 unordered_map<int64_t, int> oldAtomMatchingTrackerMap;
 vector<sp<ConditionTracker>> oldConditionTrackers;
+unordered_map<int64_t, int> oldConditionTrackerMap;
 vector<sp<MetricProducer>> oldMetricProducers;
 std::vector<sp<AnomalyTracker>> oldAnomalyTrackers;
 std::vector<sp<AlarmTracker>> oldAlarmTrackers;
@@ -74,6 +79,7 @@
         oldAtomMatchingTrackers.clear();
         oldAtomMatchingTrackerMap.clear();
         oldConditionTrackers.clear();
+        oldConditionTrackerMap.clear();
         oldMetricProducers.clear();
         oldAnomalyTrackers.clear();
         oldAlarmTrackers.clear();
@@ -92,8 +98,8 @@
     return initStatsdConfig(
             key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
             timeBaseNs, timeBaseNs, allTagIds, oldAtomMatchingTrackers, oldAtomMatchingTrackerMap,
-            oldConditionTrackers, oldMetricProducers, oldAnomalyTrackers, oldAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            oldConditionTrackers, oldConditionTrackerMap, oldMetricProducers, oldAnomalyTrackers,
+            oldAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
             activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
             metricsWithActivation, noReportMetricIds);
 }
@@ -143,6 +149,30 @@
     EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE);
 }
 
+TEST_F(ConfigUpdateTest, TestSimpleMatcherNew) {
+    StatsdConfig config;
+    AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10);
+    *config.add_atom_matcher() = matcher;
+
+    EXPECT_TRUE(initConfig(config));
+
+    StatsdConfig newConfig;
+    // Different id, so should be a new matcher.
+    AtomMatcher newMatcher = CreateSimpleAtomMatcher("DIFFERENT_NAME", /*atom=*/10);
+    int64_t matcherId = newMatcher.id();
+    EXPECT_NE(matcherId, matcher.id());
+    *newConfig.add_atom_matcher() = newMatcher;
+
+    vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    newAtomMatchingTrackerMap[matcherId] = 0;
+    EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap,
+                                             oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                             matchersToUpdate, cycleTracker));
+    EXPECT_EQ(matchersToUpdate[0], UPDATE_NEW);
+}
+
 TEST_F(ConfigUpdateTest, TestCombinationMatcherPreserve) {
     StatsdConfig config;
     AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10);
@@ -337,9 +367,10 @@
     set<int> newTagIds;
     unordered_map<int64_t, int> newAtomMatchingTrackerMap;
     vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers;
-    EXPECT_TRUE(updateAtomTrackers(newConfig, uidMap, oldAtomMatchingTrackerMap,
-                                   oldAtomMatchingTrackers, newTagIds, newAtomMatchingTrackerMap,
-                                   newAtomMatchingTrackers));
+    set<int64_t> replacedMatchers;
+    EXPECT_TRUE(updateAtomMatchingTrackers(
+            newConfig, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, newTagIds,
+            newAtomMatchingTrackerMap, newAtomMatchingTrackers, replacedMatchers));
 
     ASSERT_EQ(newTagIds.size(), 3);
     EXPECT_EQ(newTagIds.count(10), 1);
@@ -370,15 +401,488 @@
     EXPECT_NE(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(combination2Id)],
               newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(combination2Id)]);
 
-    // Validation, make sure the matchers have the proper ids. Could do more checks here.
+    // Validation, make sure the matchers have the proper ids/indices. Could do more checks here.
     EXPECT_EQ(newAtomMatchingTrackers[0]->getId(), combination3Id);
+    EXPECT_EQ(newAtomMatchingTrackers[0]->mIndex, 0);
     EXPECT_EQ(newAtomMatchingTrackers[1]->getId(), simple2Id);
+    EXPECT_EQ(newAtomMatchingTrackers[1]->mIndex, 1);
     EXPECT_EQ(newAtomMatchingTrackers[2]->getId(), combination2Id);
+    EXPECT_EQ(newAtomMatchingTrackers[2]->mIndex, 2);
     EXPECT_EQ(newAtomMatchingTrackers[3]->getId(), simple1Id);
+    EXPECT_EQ(newAtomMatchingTrackers[3]->mIndex, 3);
     EXPECT_EQ(newAtomMatchingTrackers[4]->getId(), simple4Id);
+    EXPECT_EQ(newAtomMatchingTrackers[4]->mIndex, 4);
     EXPECT_EQ(newAtomMatchingTrackers[5]->getId(), combination1Id);
+    EXPECT_EQ(newAtomMatchingTrackers[5]->mIndex, 5);
+
+    // Verify child indices of Combination Matchers are correct.
+    CombinationAtomMatchingTracker* combinationTracker1 =
+            static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[5].get());
+    vector<int>* childMatchers = &combinationTracker1->mChildren;
+    EXPECT_EQ(childMatchers->size(), 1);
+    EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 3), childMatchers->end());
+
+    CombinationAtomMatchingTracker* combinationTracker2 =
+            static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[2].get());
+    childMatchers = &combinationTracker2->mChildren;
+    EXPECT_EQ(childMatchers->size(), 2);
+    EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 1), childMatchers->end());
+    EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 3), childMatchers->end());
+
+    CombinationAtomMatchingTracker* combinationTracker3 =
+            static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[0].get());
+    childMatchers = &combinationTracker3->mChildren;
+    EXPECT_EQ(childMatchers->size(), 2);
+    EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 1), childMatchers->end());
+    EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 4), childMatchers->end());
+
+    // Expect replacedMatchers to have simple2 and combination2
+    ASSERT_EQ(replacedMatchers.size(), 2);
+    EXPECT_NE(replacedMatchers.find(simple2Id), replacedMatchers.end());
+    EXPECT_NE(replacedMatchers.find(combination2Id), replacedMatchers.end());
 }
 
+TEST_F(ConfigUpdateTest, TestSimpleConditionPreserve) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    newConditionTrackerMap[predicate.id()] = 0;
+    EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleConditionReplace) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Modify the predicate.
+    config.mutable_predicate(0)->mutable_simple_predicate()->set_count_nesting(true);
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    newConditionTrackerMap[predicate.id()] = 0;
+    EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleConditionDepsChange) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    int64_t startMatcherId = startMatcher.id();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Start matcher was replaced.
+    set<int64_t> replacedMatchers;
+    replacedMatchers.insert(startMatcherId);
+
+    vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    newConditionTrackerMap[predicate.id()] = 0;
+    EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationConditionPreserve) {
+    StatsdConfig config;
+    AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = screenOnMatcher;
+    AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = screenOffMatcher;
+
+    Predicate simple1 = CreateScreenIsOnPredicate();
+    *config.add_predicate() = simple1;
+    Predicate simple2 = CreateScreenIsOffPredicate();
+    *config.add_predicate() = simple2;
+
+    Predicate combination1;
+    combination1.set_id(StringToId("COMBINATION1"));
+    Predicate_Combination* combinationInternal = combination1.mutable_combination();
+    combinationInternal->set_operation(LogicalOperation::NAND);
+    combinationInternal->add_predicate(simple1.id());
+    combinationInternal->add_predicate(simple2.id());
+    *config.add_predicate() = combination1;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Same predicates, different order
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    *newConfig.add_predicate() = combination1;
+    newConditionTrackerMap[combination1.id()] = 0;
+    *newConfig.add_predicate() = simple2;
+    newConditionTrackerMap[simple2.id()] = 1;
+    *newConfig.add_predicate() = simple1;
+    newConditionTrackerMap[simple1.id()] = 2;
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination. It should recurse the two child predicates and preserve all 3.
+    EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE);
+    EXPECT_EQ(conditionsToUpdate[1], UPDATE_PRESERVE);
+    EXPECT_EQ(conditionsToUpdate[2], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationConditionReplace) {
+    StatsdConfig config;
+    AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = screenOnMatcher;
+    AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = screenOffMatcher;
+
+    Predicate simple1 = CreateScreenIsOnPredicate();
+    *config.add_predicate() = simple1;
+    Predicate simple2 = CreateScreenIsOffPredicate();
+    *config.add_predicate() = simple2;
+
+    Predicate combination1;
+    combination1.set_id(StringToId("COMBINATION1"));
+    Predicate_Combination* combinationInternal = combination1.mutable_combination();
+    combinationInternal->set_operation(LogicalOperation::NAND);
+    combinationInternal->add_predicate(simple1.id());
+    combinationInternal->add_predicate(simple2.id());
+    *config.add_predicate() = combination1;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Changing the logical operation changes the predicate definition, so it should be replaced.
+    combination1.mutable_combination()->set_operation(LogicalOperation::OR);
+
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    *newConfig.add_predicate() = combination1;
+    newConditionTrackerMap[combination1.id()] = 0;
+    *newConfig.add_predicate() = simple2;
+    newConditionTrackerMap[simple2.id()] = 1;
+    *newConfig.add_predicate() = simple1;
+    newConditionTrackerMap[simple1.id()] = 2;
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination. The simple conditions should not be evaluated.
+    EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+    EXPECT_EQ(conditionsToUpdate[1], UPDATE_UNKNOWN);
+    EXPECT_EQ(conditionsToUpdate[2], UPDATE_UNKNOWN);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationConditionDepsChange) {
+    StatsdConfig config;
+    AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = screenOnMatcher;
+    AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = screenOffMatcher;
+
+    Predicate simple1 = CreateScreenIsOnPredicate();
+    *config.add_predicate() = simple1;
+    Predicate simple2 = CreateScreenIsOffPredicate();
+    *config.add_predicate() = simple2;
+
+    Predicate combination1;
+    combination1.set_id(StringToId("COMBINATION1"));
+    Predicate_Combination* combinationInternal = combination1.mutable_combination();
+    combinationInternal->set_operation(LogicalOperation::NAND);
+    combinationInternal->add_predicate(simple1.id());
+    combinationInternal->add_predicate(simple2.id());
+    *config.add_predicate() = combination1;
+
+    EXPECT_TRUE(initConfig(config));
+
+    simple2.mutable_simple_predicate()->set_count_nesting(false);
+
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    *newConfig.add_predicate() = combination1;
+    newConditionTrackerMap[combination1.id()] = 0;
+    *newConfig.add_predicate() = simple2;
+    newConditionTrackerMap[simple2.id()] = 1;
+    *newConfig.add_predicate() = simple1;
+    newConditionTrackerMap[simple1.id()] = 2;
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination. Simple2 and combination1 must be evaluated.
+    EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+    EXPECT_EQ(conditionsToUpdate[1], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateConditions) {
+    StatsdConfig config;
+
+    // Add atom matchers. These are mostly needed for initStatsdConfig
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher();
+    int64_t matcher5Id = matcher5.id();
+    *config.add_atom_matcher() = matcher5;
+
+    AtomMatcher matcher6 = CreateBatterySaverModeStopAtomMatcher();
+    int64_t matcher6Id = matcher6.id();
+    *config.add_atom_matcher() = matcher6;
+
+    // Add the predicates.
+    // Will be preserved.
+    Predicate simple1 = CreateScreenIsOnPredicate();
+    int64_t simple1Id = simple1.id();
+    *config.add_predicate() = simple1;
+
+    // Will be preserved.
+    Predicate simple2 = CreateScheduledJobPredicate();
+    int64_t simple2Id = simple2.id();
+    *config.add_predicate() = simple2;
+
+    // Will be replaced.
+    Predicate simple3 = CreateBatterySaverModePredicate();
+    int64_t simple3Id = simple3.id();
+    *config.add_predicate() = simple3;
+
+    // Will be preserved
+    Predicate combination1;
+    combination1.set_id(StringToId("COMBINATION1"));
+    combination1.mutable_combination()->set_operation(LogicalOperation::AND);
+    combination1.mutable_combination()->add_predicate(simple1Id);
+    combination1.mutable_combination()->add_predicate(simple2Id);
+    int64_t combination1Id = combination1.id();
+    *config.add_predicate() = combination1;
+
+    // Will be replaced since simple3 will be replaced.
+    Predicate combination2;
+    combination2.set_id(StringToId("COMBINATION2"));
+    combination2.mutable_combination()->set_operation(LogicalOperation::OR);
+    combination2.mutable_combination()->add_predicate(simple1Id);
+    combination2.mutable_combination()->add_predicate(simple3Id);
+    int64_t combination2Id = combination2.id();
+    *config.add_predicate() = combination2;
+
+    // Will be removed.
+    Predicate combination3;
+    combination3.set_id(StringToId("COMBINATION3"));
+    combination3.mutable_combination()->set_operation(LogicalOperation::NOT);
+    combination3.mutable_combination()->add_predicate(simple2Id);
+    int64_t combination3Id = combination3.id();
+    *config.add_predicate() = combination3;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Mark marcher 5 as replaced. Causes simple3, and therefore combination2 to be replaced.
+    set<int64_t> replacedMatchers;
+    replacedMatchers.insert(matcher6Id);
+
+    // Change the condition of simple1 to true.
+    ASSERT_EQ(oldConditionTrackers[0]->getConditionId(), simple1Id);
+    LogEvent event(/*uid=*/0, /*pid=*/0);  // Empty event is fine since there are no dimensions.
+    // Mark the stop matcher as matched, condition should be false.
+    vector<MatchingState> eventMatcherValues(6, MatchingState::kNotMatched);
+    eventMatcherValues[1] = MatchingState::kMatched;
+    vector<ConditionState> tmpConditionCache(6, ConditionState::kNotEvaluated);
+    vector<bool> conditionChangeCache(6, false);
+    oldConditionTrackers[0]->evaluateCondition(event, eventMatcherValues, oldConditionTrackers,
+                                               tmpConditionCache, conditionChangeCache);
+    EXPECT_EQ(tmpConditionCache[0], ConditionState::kFalse);
+    EXPECT_EQ(conditionChangeCache[0], true);
+
+    // New combination matcher. Should have an initial condition of true since it is NOT(simple1).
+    Predicate combination4;
+    combination4.set_id(StringToId("COMBINATION4"));
+    combination4.mutable_combination()->set_operation(LogicalOperation::NOT);
+    combination4.mutable_combination()->add_predicate(simple1Id);
+    int64_t combination4Id = combination4.id();
+    *config.add_predicate() = combination4;
+
+    // Map the matchers in reverse order to force the indices to change.
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    const int matcher6Index = 0;
+    newAtomMatchingTrackerMap[matcher6Id] = 0;
+    const int matcher5Index = 1;
+    newAtomMatchingTrackerMap[matcher5Id] = 1;
+    const int matcher4Index = 2;
+    newAtomMatchingTrackerMap[matcher4Id] = 2;
+    const int matcher3Index = 3;
+    newAtomMatchingTrackerMap[matcher3Id] = 3;
+    const int matcher2Index = 4;
+    newAtomMatchingTrackerMap[matcher2Id] = 4;
+    const int matcher1Index = 5;
+    newAtomMatchingTrackerMap[matcher1Id] = 5;
+
+    StatsdConfig newConfig;
+    *newConfig.add_predicate() = simple3;
+    const int simple3Index = 0;
+    *newConfig.add_predicate() = combination2;
+    const int combination2Index = 1;
+    *newConfig.add_predicate() = combination4;
+    const int combination4Index = 2;
+    *newConfig.add_predicate() = simple2;
+    const int simple2Index = 3;
+    *newConfig.add_predicate() = combination1;
+    const int combination1Index = 4;
+    *newConfig.add_predicate() = simple1;
+    const int simple1Index = 5;
+
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    vector<sp<ConditionTracker>> newConditionTrackers;
+    unordered_map<int, vector<int>> trackerToConditionMap;
+    std::vector<ConditionState> conditionCache;
+    std::set<int64_t> replacedConditions;
+    EXPECT_TRUE(updateConditions(key, newConfig, newAtomMatchingTrackerMap, replacedMatchers,
+                                 oldConditionTrackerMap, oldConditionTrackers,
+                                 newConditionTrackerMap, newConditionTrackers,
+                                 trackerToConditionMap, conditionCache, replacedConditions));
+
+    unordered_map<int64_t, int> expectedConditionTrackerMap = {
+            {simple1Id, simple1Index},           {simple2Id, simple2Index},
+            {simple3Id, simple3Index},           {combination1Id, combination1Index},
+            {combination2Id, combination2Index}, {combination4Id, combination4Index},
+    };
+    EXPECT_THAT(newConditionTrackerMap, ContainerEq(expectedConditionTrackerMap));
+
+    ASSERT_EQ(newConditionTrackers.size(), 6);
+    // Make sure all conditions are initialized:
+    for (const sp<ConditionTracker>& tracker : newConditionTrackers) {
+        EXPECT_TRUE(tracker->mInitialized);
+    }
+
+    // Make sure preserved conditions are the same.
+    EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(simple1Id)],
+              newConditionTrackers[newConditionTrackerMap.at(simple1Id)]);
+    EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(simple2Id)],
+              newConditionTrackers[newConditionTrackerMap.at(simple2Id)]);
+    EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(combination1Id)],
+              newConditionTrackers[newConditionTrackerMap.at(combination1Id)]);
+
+    // Make sure replaced conditions are different and included in replacedConditions.
+    EXPECT_NE(oldConditionTrackers[oldConditionTrackerMap.at(simple3Id)],
+              newConditionTrackers[newConditionTrackerMap.at(simple3Id)]);
+    EXPECT_NE(oldConditionTrackers[oldConditionTrackerMap.at(combination2Id)],
+              newConditionTrackers[newConditionTrackerMap.at(combination2Id)]);
+    EXPECT_THAT(replacedConditions, ContainerEq(set({simple3Id, combination2Id})));
+
+    // Verify the trackerToConditionMap
+    ASSERT_EQ(trackerToConditionMap.size(), 6);
+    const vector<int>& matcher1Conditions = trackerToConditionMap[matcher1Index];
+    EXPECT_THAT(matcher1Conditions, UnorderedElementsAre(simple1Index, combination1Index,
+                                                         combination2Index, combination4Index));
+    const vector<int>& matcher2Conditions = trackerToConditionMap[matcher2Index];
+    EXPECT_THAT(matcher2Conditions, UnorderedElementsAre(simple1Index, combination1Index,
+                                                         combination2Index, combination4Index));
+    const vector<int>& matcher3Conditions = trackerToConditionMap[matcher3Index];
+    EXPECT_THAT(matcher3Conditions, UnorderedElementsAre(simple2Index, combination1Index));
+    const vector<int>& matcher4Conditions = trackerToConditionMap[matcher4Index];
+    EXPECT_THAT(matcher4Conditions, UnorderedElementsAre(simple2Index, combination1Index));
+    const vector<int>& matcher5Conditions = trackerToConditionMap[matcher5Index];
+    EXPECT_THAT(matcher5Conditions, UnorderedElementsAre(simple3Index, combination2Index));
+    const vector<int>& matcher6Conditions = trackerToConditionMap[matcher6Index];
+    EXPECT_THAT(matcher6Conditions, UnorderedElementsAre(simple3Index, combination2Index));
+
+    // Verify the conditionCache. Specifically, simple1 is false and combination4 is true.
+    ASSERT_EQ(conditionCache.size(), 6);
+    EXPECT_EQ(conditionCache[simple1Index], ConditionState::kFalse);
+    EXPECT_EQ(conditionCache[simple2Index], ConditionState::kUnknown);
+    EXPECT_EQ(conditionCache[simple3Index], ConditionState::kUnknown);
+    EXPECT_EQ(conditionCache[combination1Index], ConditionState::kUnknown);
+    EXPECT_EQ(conditionCache[combination2Index], ConditionState::kUnknown);
+    EXPECT_EQ(conditionCache[combination4Index], ConditionState::kTrue);
+
+    // Verify tracker indices/ids are correct.
+    EXPECT_EQ(newConditionTrackers[simple1Index]->getConditionId(), simple1Id);
+    EXPECT_EQ(newConditionTrackers[simple1Index]->mIndex, simple1Index);
+    EXPECT_TRUE(newConditionTrackers[simple1Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[simple2Index]->getConditionId(), simple2Id);
+    EXPECT_EQ(newConditionTrackers[simple2Index]->mIndex, simple2Index);
+    EXPECT_TRUE(newConditionTrackers[simple2Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[simple3Index]->getConditionId(), simple3Id);
+    EXPECT_EQ(newConditionTrackers[simple3Index]->mIndex, simple3Index);
+    EXPECT_TRUE(newConditionTrackers[simple3Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[combination1Index]->getConditionId(), combination1Id);
+    EXPECT_EQ(newConditionTrackers[combination1Index]->mIndex, combination1Index);
+    EXPECT_FALSE(newConditionTrackers[combination1Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[combination2Index]->getConditionId(), combination2Id);
+    EXPECT_EQ(newConditionTrackers[combination2Index]->mIndex, combination2Index);
+    EXPECT_FALSE(newConditionTrackers[combination2Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[combination4Index]->getConditionId(), combination4Id);
+    EXPECT_EQ(newConditionTrackers[combination4Index]->mIndex, combination4Index);
+    EXPECT_FALSE(newConditionTrackers[combination4Index]->IsSimpleCondition());
+
+    // Verify preserved trackers have indices updated.
+    SimpleConditionTracker* simpleTracker1 =
+            static_cast<SimpleConditionTracker*>(newConditionTrackers[simple1Index].get());
+    EXPECT_EQ(simpleTracker1->mStartLogMatcherIndex, matcher1Index);
+    EXPECT_EQ(simpleTracker1->mStopLogMatcherIndex, matcher2Index);
+    EXPECT_EQ(simpleTracker1->mStopAllLogMatcherIndex, -1);
+
+    SimpleConditionTracker* simpleTracker2 =
+            static_cast<SimpleConditionTracker*>(newConditionTrackers[simple2Index].get());
+    EXPECT_EQ(simpleTracker2->mStartLogMatcherIndex, matcher3Index);
+    EXPECT_EQ(simpleTracker2->mStopLogMatcherIndex, matcher4Index);
+    EXPECT_EQ(simpleTracker2->mStopAllLogMatcherIndex, -1);
+
+    CombinationConditionTracker* combinationTracker1 = static_cast<CombinationConditionTracker*>(
+            newConditionTrackers[combination1Index].get());
+    EXPECT_THAT(combinationTracker1->mChildren, UnorderedElementsAre(simple1Index, simple2Index));
+    EXPECT_THAT(combinationTracker1->mUnSlicedChildren,
+                UnorderedElementsAre(simple1Index, simple2Index));
+    EXPECT_THAT(combinationTracker1->mSlicedChildren, IsEmpty());
+}
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
index d6db4c1..e6583c9 100644
--- a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
+++ b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
@@ -384,8 +384,9 @@
     StatsdConfig config = buildConfigWithDifferentPredicates();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
@@ -400,9 +401,9 @@
 
     EXPECT_TRUE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers,
+            allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
             activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
             metricsWithActivation, noReportMetricIds));
     ASSERT_EQ(4u, allMetricProducers.size());
@@ -433,8 +434,9 @@
     StatsdConfig config = buildGoodConfig();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
@@ -449,9 +451,9 @@
 
     EXPECT_TRUE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers,
+            allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
             activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
             metricsWithActivation, noReportMetricIds));
     ASSERT_EQ(1u, allMetricProducers.size());
@@ -470,8 +472,9 @@
     StatsdConfig config = buildDimensionMetricsWithMultiTags();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
@@ -486,9 +489,9 @@
 
     EXPECT_FALSE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers,
+            allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
             activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
             metricsWithActivation, noReportMetricIds));
 }
@@ -501,8 +504,9 @@
     StatsdConfig config = buildCircleMatchers();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
@@ -517,9 +521,9 @@
 
     EXPECT_FALSE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers,
+            allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
             activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
             metricsWithActivation, noReportMetricIds));
 }
@@ -532,8 +536,9 @@
     StatsdConfig config = buildMissingMatchers();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
@@ -547,9 +552,9 @@
     std::set<int64_t> noReportMetricIds;
     EXPECT_FALSE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers,
+            allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
             activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
             metricsWithActivation, noReportMetricIds));
 }
@@ -562,8 +567,9 @@
     StatsdConfig config = buildMissingPredicate();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
@@ -577,9 +583,9 @@
     std::set<int64_t> noReportMetricIds;
     EXPECT_FALSE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers,
+            allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
             activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
             metricsWithActivation, noReportMetricIds));
 }
@@ -592,8 +598,9 @@
     StatsdConfig config = buildCirclePredicates();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
@@ -608,9 +615,9 @@
 
     EXPECT_FALSE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers,
+            allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
             activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
             metricsWithActivation, noReportMetricIds));
 }
@@ -623,8 +630,9 @@
     StatsdConfig config = buildAlertWithUnknownMetric();
     set<int> allTagIds;
     vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
-    unordered_map<int64_t, int> logTrackerMap;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
     vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
     vector<sp<MetricProducer>> allMetricProducers;
     std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
     std::vector<sp<AlarmTracker>> allAlarmTrackers;
@@ -639,9 +647,9 @@
 
     EXPECT_FALSE(initStatsdConfig(
             kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, logTrackerMap,
-            allConditionTrackers, allMetricProducers, allAnomalyTrackers, allAlarmTrackers,
-            conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, allAnomalyTrackers,
+            allAlarmTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap,
             activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, alertTrackerMap,
             metricsWithActivation, noReportMetricIds));
 }
@@ -649,6 +657,7 @@
 TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerInvalidMatcher) {
     sp<UidMap> uidMap = new UidMap();
     AtomMatcher matcher;
+    // Matcher has no contents_case (simple/combination), so it is invalid.
     matcher.set_id(21);
     EXPECT_EQ(createAtomMatchingTracker(matcher, 0, uidMap), nullptr);
 }
@@ -699,6 +708,65 @@
     ASSERT_EQ(atomIds.size(), 0);
 }
 
+TEST(MetricsManagerTest, TestCreateConditionTrackerInvalid) {
+    const ConfigKey key(123, 456);
+    // Predicate has no contents_case (simple/combination), so it is invalid.
+    Predicate predicate;
+    predicate.set_id(21);
+    unordered_map<int64_t, int> atomTrackerMap;
+    EXPECT_EQ(createConditionTracker(key, predicate, 0, atomTrackerMap), nullptr);
+}
+
+TEST(MetricsManagerTest, TestCreateConditionTrackerSimple) {
+    int index = 1;
+    int64_t id = 987;
+    const ConfigKey key(123, 456);
+
+    int startMatcherIndex = 2, stopMatcherIndex = 0, stopAllMatcherIndex = 1;
+    int64_t startMatcherId = 246, stopMatcherId = 153, stopAllMatcherId = 975;
+
+    Predicate predicate;
+    predicate.set_id(id);
+    SimplePredicate* simplePredicate = predicate.mutable_simple_predicate();
+    simplePredicate->set_start(startMatcherId);
+    simplePredicate->set_stop(stopMatcherId);
+    simplePredicate->set_stop_all(stopAllMatcherId);
+
+    unordered_map<int64_t, int> atomTrackerMap;
+    atomTrackerMap[startMatcherId] = startMatcherIndex;
+    atomTrackerMap[stopMatcherId] = stopMatcherIndex;
+    atomTrackerMap[stopAllMatcherId] = stopAllMatcherIndex;
+
+    sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap);
+    EXPECT_EQ(tracker->getConditionId(), id);
+    EXPECT_EQ(tracker->isSliced(), false);
+    EXPECT_TRUE(tracker->IsSimpleCondition());
+    const set<int>& interestedMatchers = tracker->getAtomMatchingTrackerIndex();
+    ASSERT_EQ(interestedMatchers.size(), 3);
+    ASSERT_EQ(interestedMatchers.count(startMatcherIndex), 1);
+    ASSERT_EQ(interestedMatchers.count(stopMatcherIndex), 1);
+    ASSERT_EQ(interestedMatchers.count(stopAllMatcherIndex), 1);
+}
+
+TEST(MetricsManagerTest, TestCreateConditionTrackerCombination) {
+    int index = 1;
+    int64_t id = 987;
+    const ConfigKey key(123, 456);
+
+    Predicate predicate;
+    predicate.set_id(id);
+    Predicate_Combination* combinationPredicate = predicate.mutable_combination();
+    combinationPredicate->set_operation(LogicalOperation::AND);
+    combinationPredicate->add_predicate(888);
+    combinationPredicate->add_predicate(777);
+
+    // Combination conditions must be initialized to set most state.
+    unordered_map<int64_t, int> atomTrackerMap;
+    sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap);
+    EXPECT_EQ(tracker->getConditionId(), id);
+    EXPECT_FALSE(tracker->IsSimpleCondition());
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/uinput/Android.bp b/cmds/uinput/Android.bp
new file mode 100644
index 0000000..0d7fed2
--- /dev/null
+++ b/cmds/uinput/Android.bp
@@ -0,0 +1,18 @@
+// Copyright 2020 The Android Open Source Project
+//
+
+java_binary {
+    name: "uinput",
+    wrapper: "uinput",
+    srcs: ["**/*.java",
+           ":uinputcommand_aidl"
+    ],
+    required: ["libuinputcommand_jni"],
+}
+
+filegroup {
+    name: "uinputcommand_aidl",
+    srcs: [
+        "src/com/android/commands/uinput/InputAbsInfo.aidl",
+    ],
+}
\ No newline at end of file
diff --git a/cmds/uinput/MODULE_LICENSE_APACHE2 b/cmds/uinput/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cmds/uinput/MODULE_LICENSE_APACHE2
diff --git a/cmds/uinput/NOTICE b/cmds/uinput/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/cmds/uinput/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/cmds/uinput/README.md b/cmds/uinput/README.md
new file mode 100644
index 0000000..47e1dad
--- /dev/null
+++ b/cmds/uinput/README.md
@@ -0,0 +1,166 @@
+# Usage
+##  Two options to use the uinput command:
+### 1. Interactive through stdin:
+type `uinput -` into the terminal, then type/paste commands to send to the binary.
+Use Ctrl+D to signal end of stream to the binary (EOF).
+
+This mode can be also used from an app to send uinput events.
+For an example, see the cts test case at: [InputTestCase.java][2]
+
+When using another program to control uinput in interactive mode, registering a
+new input device (for example, a bluetooth joystick) should be the first step.
+After the device is added, you need to wait for the _onInputDeviceAdded_
+(see [InputDeviceListener][1]) notification before issuing commands
+to the device.
+Failure to do so will cause missed events and inconsistent behavior.
+
+### 2. Using a file as an input:
+type `uinput <filename>`, and the file will be used an an input to the binary.
+You must add a sufficient delay after a "register" command to ensure device
+is ready. The interactive mode is the recommended method of communicating
+with the uinput binary.
+
+All of the input commands should be in pseudo-JSON format as documented below.
+See examples [here][3].
+
+The file can have multiple commands one after the other (which is not strictly
+legal JSON format, as this would imply multiple root elements).
+
+## Command description
+
+1. `register`
+Register a new uinput device
+
+| Field         | Type          | Description                |
+|:-------------:|:-------------:|:-------------------------- |
+| id            | integer       | Device id                  |
+| command       | string        | Must be set to "register"  |
+| name          | string        | Device name                |
+| vid           | 16-bit integer| Vendor id                  |
+| pid           | 16-bit integer| Product id                 |
+| bus           | string        | Bus that device should use |
+| configuration | int array     | uinput device configuration|
+| ff_effects_max| integer       | ff_effects_max value       |
+| abs_info      | array         | ABS axes information       |
+
+Device ID is used for matching the subsequent commands to a specific device
+to avoid ambiguity when multiple devices are registered.
+
+Device bus is used to determine how the uinput device is connected to the host.
+The options are "usb" and "bluetooth".
+
+Device configuration is used to configure uinput device.  "type" field provides the UI_SET_*
+control code, and data is a vector of control values to be sent to uinput device, depends on
+the control code.
+
+| Field         | Type          | Description                |
+|:-------------:|:-------------:|:-------------------------- |
+| type          | integer       | UI_SET_ control type       |
+| data          | int array     | control values             |
+
+Device ff_effects_max must be provided if FFBIT is set.
+
+Device abs_info fields are provided to set the device axes information. It is an array of below
+objects:
+| Field         | Type          | Description                |
+|:-------------:|:-------------:|:-------------------------- |
+| code          | integer       | Axis code                  |
+| info          | object        | ABS information object     |
+
+ABS information object is defined as below:
+| Field         | Type          | Description                |
+|:-------------:|:-------------:|:-------------------------- |
+| value         | integer       | Latest reported value      |
+| minimum       | integer       | Minimum value for the axis |
+| maximum       | integer       | Maximum value for the axis |
+| fuzz          | integer       | fuzz value for noise filter|
+| flat          | integer       | values to be discarded     |
+| resolution    | integer       | resolution of axis         |
+
+See [struct input_absinfo][4]) definitions.
+
+Example:
+```json
+
+{
+  "id": 1,
+  "command": "register",
+  "name": "Keyboard (Test)",
+  "vid": 0x18d2,
+  "pid": 0x2c42,
+  "bus": "usb",
+  "configuration":[
+        {"type":100, "data":[1, 21]},  // UI_SET_EVBIT : EV_KEY and EV_FF
+        {"type":101, "data":[11, 2, 3, 4]},   // UI_SET_KEYBIT : KEY_0 KEY_1 KEY_2 KEY_3
+        {"type":107, "data":[80]}    //  UI_SET_FFBIT : FF_RUMBLE
+  ],
+  "ff_effects_max" : 1,
+  "abs_info": [
+        {"code":1, "info": {"value":20, "minimum":-255,
+                            "maximum":255, "fuzz":0, "flat":0, "resolution":1}
+        },
+        {"code":8, "info": {"value":-50, "minimum":-255,
+                            "maximum":255, "fuzz":0, "flat":0, "resolution":1}
+        }
+  ]
+}
+
+```
+2. `delay`
+Add a delay to command processing
+
+| Field         | Type          | Description                |
+|:-------------:|:-------------:|:-------------------------- |
+| id            | integer       | Device id                  |
+| command       | string        | Must be set to "delay"     |
+| duration      | integer       | Delay in milliseconds      |
+
+Example:
+```json
+{
+  "id": 1,
+  "command": "delay",
+  "duration": 10
+}
+```
+
+3. `inject`
+Send an array of uinput event packets [type, code, value] to the uinput device
+
+| Field         | Type          | Description                |
+|:-------------:|:-------------:|:-------------------------- |
+| id            | integer       | Device id                  |
+| command       | string        | Must be set to "inject"    |
+| events        | integer array | events to inject           |
+
+The "events" parameter is an array of integers, encapsulates evdev input_event type, code and value,
+see the example below.
+
+Example:
+```json
+{
+  "id": 1,
+  "command": "inject",
+  "events": [0x01, 0xb,  0x1,   // EV_KEY, KEY_0, DOWN
+             0x00, 0x00, 0x00,  // EV_SYN, SYN_REPORT, 0
+             0x01, 0x0b, 0x00,  // EV_KEY, KEY_0, UP
+             0x00, 0x00, 0x00,  // EV_SYN, SYN_REPORT, 0
+             0x01, 0x2,  0x1,   // EV_KEY, KEY_1, DOWN
+             0x00, 0x00, 0x01,  // EV_SYN, SYN_REPORT, 0
+             0x01, 0x02, 0x00,  // EV_KEY, KEY_1, UP
+             0x00, 0x00, 0x01   // EV_SYN, SYN_REPORT, 0
+            ]
+}
+```
+
+### Notes
+1. As soon as EOF is reached (either in interactive mode, or in file mode),
+the device that was created will be unregistered. There is no
+explicit command for unregistering a device.
+2. The `getevent` utility can used to print out the key events
+for debugging purposes.
+
+[1]: https://developer.android.com/reference/android/hardware/input/InputManager.InputDeviceListener.html
+[2]: ../../../../cts/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java
+[3]: ../../../../cts/tests/tests/hardware/res/raw/
+[4]: ../../../../bionic/libc/kernel/uapi/linux/input.h
diff --git a/cmds/uinput/jni/Android.bp b/cmds/uinput/jni/Android.bp
new file mode 100644
index 0000000..199bbbd
--- /dev/null
+++ b/cmds/uinput/jni/Android.bp
@@ -0,0 +1,23 @@
+cc_library_shared {
+    name: "libuinputcommand_jni",
+
+    srcs: [
+        "com_android_commands_uinput_Device.cpp",
+        ":uinputcommand_aidl",
+    ],
+
+    shared_libs: [
+        "libandroid",
+        "libandroid_runtime_lazy",
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libnativehelper",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.cpp b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
new file mode 100644
index 0000000..06fa2aa
--- /dev/null
+++ b/cmds/uinput/jni/com_android_commands_uinput_Device.cpp
@@ -0,0 +1,351 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "UinputCommandDevice"
+
+#include <linux/uinput.h>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <time.h>
+#include <unistd.h>
+#include <algorithm>
+#include <array>
+#include <cstdio>
+#include <cstring>
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include <android/looper.h>
+#include <android_os_Parcel.h>
+#include <jni.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
+
+#include <android-base/stringprintf.h>
+
+#include "com_android_commands_uinput_Device.h"
+
+namespace android {
+namespace uinput {
+
+using src::com::android::commands::uinput::InputAbsInfo;
+
+static constexpr const char* UINPUT_PATH = "/dev/uinput";
+
+static struct {
+    jmethodID onDeviceConfigure;
+    jmethodID onDeviceVibrating;
+    jmethodID onDeviceError;
+} gDeviceCallbackClassInfo;
+
+static void checkAndClearException(JNIEnv* env, const char* methodName) {
+    if (env->ExceptionCheck()) {
+        ALOGE("An exception was thrown by callback '%s'.", methodName);
+        env->ExceptionClear();
+    }
+}
+
+DeviceCallback::DeviceCallback(JNIEnv* env, jobject callback)
+      : mCallbackObject(env->NewGlobalRef(callback)) {
+    env->GetJavaVM(&mJavaVM);
+}
+
+DeviceCallback::~DeviceCallback() {
+    JNIEnv* env = getJNIEnv();
+    env->DeleteGlobalRef(mCallbackObject);
+}
+
+void DeviceCallback::onDeviceError() {
+    JNIEnv* env = getJNIEnv();
+    env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceError);
+    checkAndClearException(env, "onDeviceError");
+}
+
+void DeviceCallback::onDeviceConfigure(int handle) {
+    JNIEnv* env = getJNIEnv();
+    env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceConfigure, handle);
+    checkAndClearException(env, "onDeviceConfigure");
+}
+
+void DeviceCallback::onDeviceVibrating(int value) {
+    JNIEnv* env = getJNIEnv();
+    env->CallVoidMethod(mCallbackObject, gDeviceCallbackClassInfo.onDeviceVibrating, value);
+    checkAndClearException(env, "onDeviceVibrating");
+}
+
+JNIEnv* DeviceCallback::getJNIEnv() {
+    JNIEnv* env;
+    mJavaVM->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
+    return env;
+}
+
+std::unique_ptr<UinputDevice> UinputDevice::open(int32_t id, const char* name, int32_t vid,
+                                                 int32_t pid, uint16_t bus, uint32_t ffEffectsMax,
+                                                 std::unique_ptr<DeviceCallback> callback) {
+    android::base::unique_fd fd(::open(UINPUT_PATH, O_RDWR | O_NONBLOCK | O_CLOEXEC));
+    if (!fd.ok()) {
+        ALOGE("Failed to open uinput: %s", strerror(errno));
+        return nullptr;
+    }
+
+    int32_t version;
+    ::ioctl(fd, UI_GET_VERSION, &version);
+    if (version < 5) {
+        ALOGE("Kernel version %d older than 5 is not supported", version);
+        return nullptr;
+    }
+
+    struct uinput_setup setupDescriptor;
+    memset(&setupDescriptor, 0, sizeof(setupDescriptor));
+    strlcpy(setupDescriptor.name, name, UINPUT_MAX_NAME_SIZE);
+    setupDescriptor.id.version = 1;
+    setupDescriptor.id.bustype = bus;
+    setupDescriptor.id.vendor = vid;
+    setupDescriptor.id.product = pid;
+    setupDescriptor.ff_effects_max = ffEffectsMax;
+
+    // Request device configuration.
+    callback->onDeviceConfigure(fd.get());
+
+    // register the input device
+    if (::ioctl(fd, UI_DEV_SETUP, &setupDescriptor)) {
+        ALOGE("UI_DEV_SETUP ioctl failed on fd %d: %s.", fd.get(), strerror(errno));
+        return nullptr;
+    }
+
+    if (::ioctl(fd, UI_DEV_CREATE) != 0) {
+        ALOGE("Unable to create uinput device: %s.", strerror(errno));
+        return nullptr;
+    }
+
+    // using 'new' to access non-public constructor
+    return std::unique_ptr<UinputDevice>(new UinputDevice(id, std::move(fd), std::move(callback)));
+}
+
+UinputDevice::UinputDevice(int32_t id, android::base::unique_fd fd,
+                           std::unique_ptr<DeviceCallback> callback)
+      : mId(id), mFd(std::move(fd)), mDeviceCallback(std::move(callback)) {
+    ALooper* aLooper = ALooper_forThread();
+    if (aLooper == nullptr) {
+        ALOGE("Could not get ALooper, ALooper_forThread returned NULL");
+        aLooper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
+    }
+    ALooper_addFd(
+            aLooper, mFd, 0, ALOOPER_EVENT_INPUT,
+            [](int, int events, void* data) {
+                UinputDevice* d = reinterpret_cast<UinputDevice*>(data);
+                return d->handleEvents(events);
+            },
+            reinterpret_cast<void*>(this));
+    ALOGI("uinput device %d created: version = %d, fd = %d", mId, UINPUT_VERSION, mFd.get());
+}
+
+UinputDevice::~UinputDevice() {
+    ::ioctl(mFd, UI_DEV_DESTROY);
+}
+
+void UinputDevice::injectEvent(uint16_t type, uint16_t code, int32_t value) {
+    struct input_event event = {};
+    event.type = type;
+    event.code = code;
+    event.value = value;
+    timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    TIMESPEC_TO_TIMEVAL(&event.time, &ts);
+
+    if (::write(mFd, &event, sizeof(input_event)) < 0) {
+        ALOGE("Could not write event %" PRIu16 " %" PRIu16 " with value %" PRId32 " : %s", type,
+              code, value, strerror(errno));
+    }
+}
+
+int UinputDevice::handleEvents(int events) {
+    if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
+        ALOGE("uinput node was closed or an error occurred. events=0x%x", events);
+        mDeviceCallback->onDeviceError();
+        return 0;
+    }
+    struct input_event ev;
+    ssize_t ret = ::read(mFd, &ev, sizeof(ev));
+    if (ret < 0) {
+        ALOGE("Failed to read from uinput node: %s", strerror(errno));
+        mDeviceCallback->onDeviceError();
+        return 0;
+    }
+
+    switch (ev.type) {
+        case EV_UINPUT: {
+            if (ev.code == UI_FF_UPLOAD) {
+                struct uinput_ff_upload ff_upload;
+                ff_upload.request_id = ev.value;
+                ::ioctl(mFd, UI_BEGIN_FF_UPLOAD, &ff_upload);
+                ff_upload.retval = 0;
+                ::ioctl(mFd, UI_END_FF_UPLOAD, &ff_upload);
+            } else if (ev.code == UI_FF_ERASE) {
+                struct uinput_ff_erase ff_erase;
+                ff_erase.request_id = ev.value;
+                ::ioctl(mFd, UI_BEGIN_FF_ERASE, &ff_erase);
+                ff_erase.retval = 0;
+                ::ioctl(mFd, UI_END_FF_ERASE, &ff_erase);
+            }
+            break;
+        }
+        case EV_FF: {
+            ALOGI("EV_FF effect = %d value = %d", ev.code, ev.value);
+            mDeviceCallback->onDeviceVibrating(ev.value);
+            break;
+        }
+        default: {
+            ALOGI("Unhandled event type: %" PRIu32, ev.type);
+            break;
+        }
+    }
+
+    return 1;
+}
+
+} // namespace uinput
+
+std::vector<int32_t> toVector(JNIEnv* env, jintArray javaArray) {
+    std::vector<int32_t> data;
+    if (javaArray == nullptr) {
+        return data;
+    }
+
+    ScopedIntArrayRO scopedArray(env, javaArray);
+    size_t size = scopedArray.size();
+    data.reserve(size);
+    for (size_t i = 0; i < size; i++) {
+        data.push_back(static_cast<int32_t>(scopedArray[i]));
+    }
+    return data;
+}
+
+static jlong openUinputDevice(JNIEnv* env, jclass /* clazz */, jstring rawName, jint id, jint vid,
+                              jint pid, jint bus, jint ffEffectsMax, jobject callback) {
+    ScopedUtfChars name(env, rawName);
+    if (name.c_str() == nullptr) {
+        return 0;
+    }
+
+    std::unique_ptr<uinput::DeviceCallback> cb =
+            std::make_unique<uinput::DeviceCallback>(env, callback);
+
+    std::unique_ptr<uinput::UinputDevice> d =
+            uinput::UinputDevice::open(id, name.c_str(), vid, pid, bus, ffEffectsMax,
+                                       std::move(cb));
+    return reinterpret_cast<jlong>(d.release());
+}
+
+static void closeUinputDevice(JNIEnv* /* env */, jclass /* clazz */, jlong ptr) {
+    uinput::UinputDevice* d = reinterpret_cast<uinput::UinputDevice*>(ptr);
+    if (d != nullptr) {
+        delete d;
+    }
+}
+
+static void injectEvent(JNIEnv* /* env */, jclass /* clazz */, jlong ptr, jint type, jint code,
+                        jint value) {
+    uinput::UinputDevice* d = reinterpret_cast<uinput::UinputDevice*>(ptr);
+    if (d != nullptr) {
+        d->injectEvent(static_cast<uint16_t>(type), static_cast<uint16_t>(code),
+                       static_cast<int32_t>(value));
+    } else {
+        ALOGE("Could not inject event, Device* is null!");
+    }
+}
+
+static void configure(JNIEnv* env, jclass /* clazz */, jint handle, jint code,
+                      jintArray rawConfigs) {
+    std::vector<int32_t> configs = toVector(env, rawConfigs);
+    // Configure uinput device, with user specified code and value.
+    for (auto& config : configs) {
+        ::ioctl(static_cast<int>(handle), _IOW(UINPUT_IOCTL_BASE, code, int), config);
+    }
+}
+
+static void setAbsInfo(JNIEnv* env, jclass /* clazz */, jint handle, jint axisCode,
+                       jobject infoObj) {
+    Parcel* parcel = parcelForJavaObject(env, infoObj);
+    uinput::InputAbsInfo info;
+
+    info.readFromParcel(parcel);
+
+    struct uinput_abs_setup absSetup;
+    absSetup.code = axisCode;
+    absSetup.absinfo.maximum = info.maximum;
+    absSetup.absinfo.minimum = info.minimum;
+    absSetup.absinfo.value = info.value;
+    absSetup.absinfo.fuzz = info.fuzz;
+    absSetup.absinfo.flat = info.flat;
+    absSetup.absinfo.resolution = info.resolution;
+
+    ::ioctl(static_cast<int>(handle), UI_ABS_SETUP, &absSetup);
+}
+
+static JNINativeMethod sMethods[] = {
+        {"nativeOpenUinputDevice",
+         "(Ljava/lang/String;IIIII"
+         "Lcom/android/commands/uinput/Device$DeviceCallback;)J",
+         reinterpret_cast<void*>(openUinputDevice)},
+        {"nativeInjectEvent", "(JIII)V", reinterpret_cast<void*>(injectEvent)},
+        {"nativeConfigure", "(II[I)V", reinterpret_cast<void*>(configure)},
+        {"nativeSetAbsInfo", "(IILandroid/os/Parcel;)V", reinterpret_cast<void*>(setAbsInfo)},
+        {"nativeCloseUinputDevice", "(J)V", reinterpret_cast<void*>(closeUinputDevice)},
+};
+
+int register_com_android_commands_uinput_Device(JNIEnv* env) {
+    jclass clazz = env->FindClass("com/android/commands/uinput/Device$DeviceCallback");
+    if (clazz == nullptr) {
+        ALOGE("Unable to find class 'DeviceCallback'");
+        return JNI_ERR;
+    }
+
+    uinput::gDeviceCallbackClassInfo.onDeviceConfigure =
+            env->GetMethodID(clazz, "onDeviceConfigure", "(I)V");
+    uinput::gDeviceCallbackClassInfo.onDeviceVibrating =
+            env->GetMethodID(clazz, "onDeviceVibrating", "(I)V");
+    uinput::gDeviceCallbackClassInfo.onDeviceError =
+            env->GetMethodID(clazz, "onDeviceError", "()V");
+    if (uinput::gDeviceCallbackClassInfo.onDeviceConfigure == nullptr ||
+        uinput::gDeviceCallbackClassInfo.onDeviceError == nullptr ||
+        uinput::gDeviceCallbackClassInfo.onDeviceVibrating == nullptr) {
+        ALOGE("Unable to obtain onDeviceConfigure or onDeviceError or onDeviceVibrating methods");
+        return JNI_ERR;
+    }
+    return jniRegisterNativeMethods(env, "com/android/commands/uinput/Device", sMethods,
+                                    NELEM(sMethods));
+}
+
+} // namespace android
+
+jint JNI_OnLoad(JavaVM* jvm, void*) {
+    JNIEnv* env = nullptr;
+    if (jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6)) {
+        return JNI_ERR;
+    }
+
+    if (android::register_com_android_commands_uinput_Device(env) < 0) {
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_6;
+}
diff --git a/cmds/uinput/jni/com_android_commands_uinput_Device.h b/cmds/uinput/jni/com_android_commands_uinput_Device.h
new file mode 100644
index 0000000..5a9a06c
--- /dev/null
+++ b/cmds/uinput/jni/com_android_commands_uinput_Device.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+#include <vector>
+
+#include <jni.h>
+#include <linux/input.h>
+
+#include <android-base/unique_fd.h>
+#include "src/com/android/commands/uinput/InputAbsInfo.h"
+
+namespace android {
+namespace uinput {
+
+class DeviceCallback {
+public:
+    DeviceCallback(JNIEnv* env, jobject callback);
+    ~DeviceCallback();
+
+    void onDeviceOpen();
+    void onDeviceGetReport(uint32_t requestId, uint8_t reportId);
+    void onDeviceOutput(const std::vector<uint8_t>& data);
+    void onDeviceConfigure(int handle);
+    void onDeviceVibrating(int value);
+    void onDeviceError();
+
+private:
+    JNIEnv* getJNIEnv();
+    jobject mCallbackObject;
+    JavaVM* mJavaVM;
+};
+
+class UinputDevice {
+public:
+    static std::unique_ptr<UinputDevice> open(int32_t id, const char* name, int32_t vid,
+                                              int32_t pid, uint16_t bus, uint32_t ff_effects_max,
+                                              std::unique_ptr<DeviceCallback> callback);
+
+    virtual ~UinputDevice();
+
+    void injectEvent(uint16_t type, uint16_t code, int32_t value);
+    int handleEvents(int events);
+
+private:
+    UinputDevice(int32_t id, android::base::unique_fd fd, std::unique_ptr<DeviceCallback> callback);
+
+    int32_t mId;
+    android::base::unique_fd mFd;
+    std::unique_ptr<DeviceCallback> mDeviceCallback;
+};
+
+} // namespace uinput
+} // namespace android
diff --git a/cmds/uinput/src/com/android/commands/uinput/Device.java b/cmds/uinput/src/com/android/commands/uinput/Device.java
new file mode 100644
index 0000000..62bee7b
--- /dev/null
+++ b/cmds/uinput/src/com/android/commands/uinput/Device.java
@@ -0,0 +1,232 @@
+/*
+ * 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.commands.uinput;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.os.SomeArgs;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import src.com.android.commands.uinput.InputAbsInfo;
+
+/**
+ * Device class defines uinput device interfaces of device operations, for device open, close,
+ * configuration, events injection.
+ */
+public class Device {
+    private static final String TAG = "UinputDevice";
+
+    private static final int MSG_OPEN_UINPUT_DEVICE = 1;
+    private static final int MSG_CLOSE_UINPUT_DEVICE = 2;
+    private static final int MSG_INJECT_EVENT = 3;
+
+    private final int mId;
+    private final HandlerThread mThread;
+    private final DeviceHandler mHandler;
+    // mConfiguration is sparse array of ioctl code and array of values.
+    private final SparseArray<int[]> mConfiguration;
+    private final SparseArray<InputAbsInfo> mAbsInfo;
+    private final OutputStream mOutputStream;
+    private final Object mCond = new Object();
+    private long mTimeToSend;
+
+    static {
+        System.loadLibrary("uinputcommand_jni");
+    }
+
+    private static native long nativeOpenUinputDevice(String name, int id, int vid, int pid,
+            int bus, int ffEffectsMax, DeviceCallback callback);
+    private static native void nativeCloseUinputDevice(long ptr);
+    private static native void nativeInjectEvent(long ptr, int type, int code, int value);
+    private static native void nativeConfigure(int handle, int code, int[] configs);
+    private static native void nativeSetAbsInfo(int handle, int axisCode, Parcel axisParcel);
+
+    public Device(int id, String name, int vid, int pid, int bus,
+            SparseArray<int[]> configuration, int ffEffectsMax,
+            SparseArray<InputAbsInfo> absInfo) {
+        mId = id;
+        mThread = new HandlerThread("UinputDeviceHandler");
+        mThread.start();
+        mHandler = new DeviceHandler(mThread.getLooper());
+        mConfiguration = configuration;
+        mAbsInfo = absInfo;
+        mOutputStream = System.out;
+        SomeArgs args = SomeArgs.obtain();
+        args.argi1 = id;
+        args.argi2 = vid;
+        args.argi3 = pid;
+        args.argi4 = bus;
+        args.argi5 = ffEffectsMax;
+        if (name != null) {
+            args.arg1 = name;
+        } else {
+            args.arg1 = id + ":" + vid + ":" + pid;
+        }
+
+        mHandler.obtainMessage(MSG_OPEN_UINPUT_DEVICE, args).sendToTarget();
+        mTimeToSend = SystemClock.uptimeMillis();
+    }
+
+    /**
+     * Inject uinput events to device
+     *
+     * @param events  Array of raw uinput events.
+     */
+    public void injectEvent(int[] events) {
+        // if two messages are sent at identical time, they will be processed in order received
+        Message msg = mHandler.obtainMessage(MSG_INJECT_EVENT, events);
+        mHandler.sendMessageAtTime(msg, mTimeToSend);
+    }
+
+    /**
+     * Impose a delay to the device for execution.
+     *
+     * @param delay  Time to delay in unit of milliseconds.
+     */
+    public void addDelay(int delay) {
+        mTimeToSend = Math.max(SystemClock.uptimeMillis(), mTimeToSend) + delay;
+    }
+
+    /**
+     * Close an uinput device.
+     *
+     */
+    public void close() {
+        Message msg = mHandler.obtainMessage(MSG_CLOSE_UINPUT_DEVICE);
+        mHandler.sendMessageAtTime(msg, Math.max(SystemClock.uptimeMillis(), mTimeToSend) + 1);
+        try {
+            synchronized (mCond) {
+                mCond.wait();
+            }
+        } catch (InterruptedException ignore) {
+        }
+    }
+
+    private class DeviceHandler extends Handler {
+        private long mPtr;
+        private int mBarrierToken;
+
+        DeviceHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_OPEN_UINPUT_DEVICE:
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    mPtr = nativeOpenUinputDevice((String) args.arg1, args.argi1, args.argi2,
+                            args.argi3, args.argi4, args.argi5,
+                            new DeviceCallback());
+                    break;
+                case MSG_INJECT_EVENT:
+                    if (mPtr != 0) {
+                        int[] events = (int[]) msg.obj;
+                        for (int pos = 0; pos + 2 < events.length; pos += 3) {
+                            nativeInjectEvent(mPtr, events[pos], events[pos + 1], events[pos + 2]);
+                        }
+                    }
+                    break;
+                case MSG_CLOSE_UINPUT_DEVICE:
+                    if (mPtr != 0) {
+                        nativeCloseUinputDevice(mPtr);
+                        getLooper().quitSafely();
+                        mPtr = 0;
+                    } else {
+                        Log.e(TAG, "Tried to close already closed device.");
+                    }
+                    Log.i(TAG, "Device closed.");
+                    synchronized (mCond) {
+                        mCond.notify();
+                    }
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown device message");
+            }
+        }
+
+        public void pauseEvents() {
+            mBarrierToken = getLooper().myQueue().postSyncBarrier();
+        }
+
+        public void resumeEvents() {
+            getLooper().myQueue().removeSyncBarrier(mBarrierToken);
+            mBarrierToken = 0;
+        }
+    }
+
+    private class DeviceCallback {
+        public void onDeviceOpen() {
+            mHandler.resumeEvents();
+        }
+
+        public void onDeviceConfigure(int handle) {
+            for (int i = 0; i < mConfiguration.size(); i++) {
+                int key = mConfiguration.keyAt(i);
+                int[] data = mConfiguration.get(key);
+                nativeConfigure(handle, key, data);
+            }
+
+            if (mAbsInfo != null) {
+                for (int i = 0; i < mAbsInfo.size(); i++) {
+                    int key = mAbsInfo.keyAt(i);
+                    InputAbsInfo info = mAbsInfo.get(key);
+                    Parcel parcel = Parcel.obtain();
+                    info.writeToParcel(parcel, 0);
+                    parcel.setDataPosition(0);
+                    nativeSetAbsInfo(handle, key, parcel);
+                }
+            }
+        }
+
+        public void onDeviceVibrating(int value) {
+            JSONObject json = new JSONObject();
+            try {
+                json.put("reason", "vibrating");
+                json.put("id", mId);
+                json.put("status", value);
+            } catch (JSONException e) {
+                throw new RuntimeException("Could not create JSON object ", e);
+            }
+            try {
+                mOutputStream.write(json.toString().getBytes());
+                mOutputStream.flush();
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        public void onDeviceError() {
+            Log.e(TAG, "Device error occurred, closing /dev/uinput");
+            Message msg = mHandler.obtainMessage(MSG_CLOSE_UINPUT_DEVICE);
+            msg.setAsynchronous(true);
+            msg.sendToTarget();
+        }
+    }
+}
diff --git a/cmds/uinput/src/com/android/commands/uinput/Event.java b/cmds/uinput/src/com/android/commands/uinput/Event.java
new file mode 100644
index 0000000..c4ba050
--- /dev/null
+++ b/cmds/uinput/src/com/android/commands/uinput/Event.java
@@ -0,0 +1,454 @@
+/*
+ * 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.commands.uinput;
+
+import android.util.JsonReader;
+import android.util.JsonToken;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import src.com.android.commands.uinput.InputAbsInfo;
+
+/**
+ * An event is a JSON file defined action event to instruct uinput to perform a command like
+ * device registration or uinput events injection.
+ */
+public class Event {
+    private static final String TAG = "UinputEvent";
+
+    public static final String COMMAND_REGISTER = "register";
+    public static final String COMMAND_DELAY = "delay";
+    public static final String COMMAND_INJECT = "inject";
+    private static final int ABS_CNT = 64;
+
+    // These constants come from "include/uapi/linux/input.h" in the kernel
+    enum Bus {
+        USB(0x03), BLUETOOTH(0x05);
+        private final int mValue;
+
+        Bus(int value) {
+            mValue = value;
+        }
+
+        int getValue() {
+            return mValue;
+        }
+    }
+
+    private int mId;
+    private String mCommand;
+    private String mName;
+    private int mVid;
+    private int mPid;
+    private Bus mBus;
+    private int[] mInjections;
+    private SparseArray<int[]> mConfiguration;
+    private int mDuration;
+    private int mFfEffectsMax = 0;
+    private SparseArray<InputAbsInfo> mAbsInfo;
+
+    public int getId() {
+        return mId;
+    }
+
+    public String getCommand() {
+        return mCommand;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public int getVendorId() {
+        return mVid;
+    }
+
+    public int getProductId() {
+        return mPid;
+    }
+
+    public int getBus() {
+        return mBus.getValue();
+    }
+
+    public int[] getInjections() {
+        return mInjections;
+    }
+
+    public SparseArray<int[]> getConfiguration() {
+        return mConfiguration;
+    }
+
+    public int getDuration() {
+        return mDuration;
+    }
+
+    public int getFfEffectsMax() {
+        return mFfEffectsMax;
+    }
+
+    public SparseArray<InputAbsInfo>  getAbsInfo() {
+        return mAbsInfo;
+    }
+
+    /**
+     * Convert an event to String.
+     */
+    public String toString() {
+        return "Event{id=" + mId
+            + ", command=" + mCommand
+            + ", name=" + mName
+            + ", vid=" + mVid
+            + ", pid=" + mPid
+            + ", bus=" + mBus
+            + ", events=" + Arrays.toString(mInjections)
+            + ", configuration=" + mConfiguration
+            + ", duration=" + mDuration
+            + ", ff_effects_max=" + mFfEffectsMax
+            + "}";
+    }
+
+    private static class Builder {
+        private Event mEvent;
+
+        Builder() {
+            mEvent = new Event();
+        }
+
+        public void setId(int id) {
+            mEvent.mId = id;
+        }
+
+        private void setCommand(String command) {
+            mEvent.mCommand = command;
+        }
+
+        public void setName(String name) {
+            mEvent.mName = name;
+        }
+
+        public void setInjections(int[] events) {
+            mEvent.mInjections = events;
+        }
+
+        public void setConfiguration(SparseArray<int[]> configuration) {
+            mEvent.mConfiguration = configuration;
+        }
+
+        public void setVid(int vid) {
+            mEvent.mVid = vid;
+        }
+
+        public void setPid(int pid) {
+            mEvent.mPid = pid;
+        }
+
+        public void setBus(Bus bus) {
+            mEvent.mBus = bus;
+        }
+
+        public void setDuration(int duration) {
+            mEvent.mDuration = duration;
+        }
+
+        public void setFfEffectsMax(int ffEffectsMax) {
+            mEvent.mFfEffectsMax = ffEffectsMax;
+        }
+
+        public void setAbsInfo(SparseArray<InputAbsInfo> absInfo) {
+            mEvent.mAbsInfo = absInfo;
+        }
+
+        public Event build() {
+            if (mEvent.mId == -1) {
+                throw new IllegalStateException("No event id");
+            } else if (mEvent.mCommand == null) {
+                throw new IllegalStateException("Event does not contain a command");
+            }
+            if (COMMAND_REGISTER.equals(mEvent.mCommand)) {
+                if (mEvent.mConfiguration == null) {
+                    throw new IllegalStateException(
+                            "Device registration is missing configuration");
+                }
+            } else if (COMMAND_DELAY.equals(mEvent.mCommand)) {
+                if (mEvent.mDuration <= 0) {
+                    throw new IllegalStateException("Delay has missing or invalid duration");
+                }
+            } else if (COMMAND_INJECT.equals(mEvent.mCommand)) {
+                if (mEvent.mInjections  == null) {
+                    throw new IllegalStateException("Inject command is missing injection data");
+                }
+            } else {
+                throw new IllegalStateException("Unknown command " + mEvent.mCommand);
+            }
+            return mEvent;
+        }
+    }
+
+    /**
+     *  A class that parses the JSON event format from an input stream to build device events.
+     */
+    public static class Reader {
+        private JsonReader mReader;
+
+        public Reader(InputStreamReader in) {
+            mReader = new JsonReader(in);
+            mReader.setLenient(true);
+        }
+
+        /**
+         * Get next event entry from JSON file reader.
+         */
+        public Event getNextEvent() throws IOException {
+            Event e = null;
+            while (e == null && mReader.peek() != JsonToken.END_DOCUMENT) {
+                Event.Builder eb = new Event.Builder();
+                try {
+                    mReader.beginObject();
+                    while (mReader.hasNext()) {
+                        String name = mReader.nextName();
+                        switch (name) {
+                            case "id":
+                                eb.setId(readInt());
+                                break;
+                            case "command":
+                                eb.setCommand(mReader.nextString());
+                                break;
+                            case "name":
+                                eb.setName(mReader.nextString());
+                                break;
+                            case "vid":
+                                eb.setVid(readInt());
+                                break;
+                            case "pid":
+                                eb.setPid(readInt());
+                                break;
+                            case "bus":
+                                eb.setBus(readBus());
+                                break;
+                            case "events":
+                                int[] injections = readIntList().stream()
+                                            .mapToInt(Integer::intValue).toArray();
+                                eb.setInjections(injections);
+                                break;
+                            case "configuration":
+                                eb.setConfiguration(readConfiguration());
+                                break;
+                            case "ff_effects_max":
+                                eb.setFfEffectsMax(readInt());
+                                break;
+                            case "abs_info":
+                                eb.setAbsInfo(readAbsInfoArray());
+                                break;
+                            case "duration":
+                                eb.setDuration(readInt());
+                                break;
+                            default:
+                                mReader.skipValue();
+                        }
+                    }
+                    mReader.endObject();
+                } catch (IllegalStateException ex) {
+                    error("Error reading in object, ignoring.", ex);
+                    consumeRemainingElements();
+                    mReader.endObject();
+                    continue;
+                }
+                e = eb.build();
+            }
+
+            return e;
+        }
+
+        private ArrayList<Integer> readIntList() throws IOException {
+            ArrayList<Integer> data = new ArrayList<Integer>();
+            try {
+                mReader.beginArray();
+                while (mReader.hasNext()) {
+                    data.add(Integer.decode(mReader.nextString()));
+                }
+                mReader.endArray();
+            } catch (IllegalStateException | NumberFormatException e) {
+                consumeRemainingElements();
+                mReader.endArray();
+                throw new IllegalStateException("Encountered malformed data.", e);
+            }
+            return data;
+        }
+
+        private byte[] readData() throws IOException {
+            ArrayList<Integer> data = readIntList();
+            byte[] rawData = new byte[data.size()];
+            for (int i = 0; i < data.size(); i++) {
+                int d = data.get(i);
+                if ((d & 0xFF) != d) {
+                    throw new IllegalStateException("Invalid data, all values must be byte-sized");
+                }
+                rawData[i] = (byte) d;
+            }
+            return rawData;
+        }
+
+        private int readInt() throws IOException {
+            String val = mReader.nextString();
+            return Integer.decode(val);
+        }
+
+        private Bus readBus() throws IOException {
+            String val = mReader.nextString();
+            return Bus.valueOf(val.toUpperCase());
+        }
+
+        private SparseArray<int[]> readConfiguration()
+                throws IllegalStateException, IOException {
+            SparseArray<int[]> configuration = new SparseArray<>();
+            try {
+                mReader.beginArray();
+                while (mReader.hasNext()) {
+                    int type = 0;
+                    int[] data = null;
+                    mReader.beginObject();
+                    while (mReader.hasNext()) {
+                        String name = mReader.nextName();
+                        switch (name) {
+                            case "type":
+                                type = readInt();
+                                break;
+                            case "data":
+                                data = readIntList().stream()
+                                            .mapToInt(Integer::intValue).toArray();
+                                break;
+                            default:
+                                consumeRemainingElements();
+                                mReader.endObject();
+                                throw new IllegalStateException(
+                                        "Invalid key in device configuration: " + name);
+                        }
+                    }
+                    mReader.endObject();
+                    if (data != null) {
+                        configuration.put(type, data);
+                    }
+                }
+                mReader.endArray();
+            } catch (IllegalStateException | NumberFormatException e) {
+                consumeRemainingElements();
+                mReader.endArray();
+                throw new IllegalStateException("Encountered malformed data.", e);
+            }
+            return configuration;
+        }
+
+        private InputAbsInfo readAbsInfo() throws IllegalStateException, IOException {
+            InputAbsInfo absInfo = new InputAbsInfo();
+            try {
+                mReader.beginObject();
+                while (mReader.hasNext()) {
+                    String name = mReader.nextName();
+                    switch (name) {
+                        case "value":
+                            absInfo.value = readInt();
+                            break;
+                        case "minimum":
+                            absInfo.minimum = readInt();
+                            break;
+                        case "maximum":
+                            absInfo.maximum = readInt();
+                            break;
+                        case "fuzz":
+                            absInfo.fuzz = readInt();
+                            break;
+                        case "flat":
+                            absInfo.flat = readInt();
+                            break;
+                        case "resolution":
+                            absInfo.resolution = readInt();
+                            break;
+                        default:
+                            consumeRemainingElements();
+                            mReader.endObject();
+                            throw new IllegalStateException("Invalid key in abs info: " + name);
+                    }
+                }
+                mReader.endObject();
+            } catch (IllegalStateException | NumberFormatException e) {
+                consumeRemainingElements();
+                mReader.endObject();
+                throw new IllegalStateException("Encountered malformed data.", e);
+            }
+            return absInfo;
+        }
+
+        private SparseArray<InputAbsInfo> readAbsInfoArray()
+                throws IllegalStateException, IOException {
+            SparseArray<InputAbsInfo> infoArray = new SparseArray<>();
+            try {
+                mReader.beginArray();
+                while (mReader.hasNext()) {
+                    int type = 0;
+                    InputAbsInfo absInfo = null;
+                    mReader.beginObject();
+                    while (mReader.hasNext()) {
+                        String name = mReader.nextName();
+                        switch (name) {
+                            case "code":
+                                type = readInt();
+                                break;
+                            case "info":
+                                absInfo = readAbsInfo();
+                                break;
+                            default:
+                                consumeRemainingElements();
+                                mReader.endObject();
+                                throw new IllegalStateException("Invalid key in abs info array: "
+                                        + name);
+                        }
+                    }
+                    mReader.endObject();
+                    if (absInfo != null) {
+                        infoArray.put(type, absInfo);
+                    }
+                }
+                mReader.endArray();
+            } catch (IllegalStateException | NumberFormatException e) {
+                consumeRemainingElements();
+                mReader.endArray();
+                throw new IllegalStateException("Encountered malformed data.", e);
+            }
+            return infoArray;
+        }
+
+        private void consumeRemainingElements() throws IOException {
+            while (mReader.hasNext()) {
+                mReader.skipValue();
+            }
+        }
+    }
+
+    private static void error(String msg, Exception e) {
+        System.out.println(msg);
+        Log.e(TAG, msg);
+        if (e != null) {
+            Log.e(TAG, Log.getStackTraceString(e));
+        }
+    }
+}
diff --git a/cmds/uinput/src/com/android/commands/uinput/InputAbsInfo.aidl b/cmds/uinput/src/com/android/commands/uinput/InputAbsInfo.aidl
new file mode 100644
index 0000000..88c57f2
--- /dev/null
+++ b/cmds/uinput/src/com/android/commands/uinput/InputAbsInfo.aidl
@@ -0,0 +1,26 @@
+/*
+**
+** 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 src.com.android.commands.uinput;
+
+parcelable InputAbsInfo {
+    int value;
+    int minimum;
+    int maximum;
+    int fuzz;
+    int flat;
+    int resolution;
+}
diff --git a/cmds/uinput/src/com/android/commands/uinput/Uinput.java b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
new file mode 100644
index 0000000..f7601a2
--- /dev/null
+++ b/cmds/uinput/src/com/android/commands/uinput/Uinput.java
@@ -0,0 +1,140 @@
+/*
+ * 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.commands.uinput;
+
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * Uinput class encapsulates execution of "uinput" command. It parses the provided input stream
+ * parameters as JSON file format, extract event entries and perform commands of event entries.
+ * Uinput device will be created when performing registration command and used to inject events.
+ */
+public class Uinput {
+    private static final String TAG = "UINPUT";
+
+    private final Event.Reader mReader;
+    private final SparseArray<Device> mDevices;
+
+    private static void usage() {
+        error("Usage: uinput [FILE]");
+    }
+
+    /**
+     * Commandline "uinput" binary main entry
+     */
+    public static void main(String[] args) {
+        if (args.length != 1) {
+            usage();
+            System.exit(1);
+        }
+
+        InputStream stream = null;
+        try {
+            if (args[0].equals("-")) {
+                stream = System.in;
+            } else {
+                File f = new File(args[0]);
+                stream = new FileInputStream(f);
+            }
+            (new Uinput(stream)).run();
+        } catch (Exception e) {
+            error("Uinput injection failed.", e);
+            System.exit(1);
+        } finally {
+            try {
+                stream.close();
+            } catch (IOException e) {
+            }
+        }
+    }
+
+    private Uinput(InputStream in) {
+        mDevices = new SparseArray<Device>();
+        try {
+            mReader = new Event.Reader(new InputStreamReader(in, "UTF-8"));
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void run() {
+        try {
+            Event e = null;
+            while ((e = mReader.getNextEvent()) != null) {
+                process(e);
+            }
+        } catch (IOException ex) {
+            error("Error reading in events.", ex);
+        }
+
+        for (int i = 0; i < mDevices.size(); i++) {
+            mDevices.valueAt(i).close();
+        }
+    }
+
+    private void process(Event e) {
+        final int index = mDevices.indexOfKey(e.getId());
+        if (index >= 0) {
+            Device d = mDevices.valueAt(index);
+            if (Event.COMMAND_DELAY.equals(e.getCommand())) {
+                d.addDelay(e.getDuration());
+            } else if (Event.COMMAND_INJECT.equals(e.getCommand())) {
+                d.injectEvent(e.getInjections());
+            } else {
+                if (Event.COMMAND_REGISTER.equals(e.getCommand())) {
+                    error("Device id=" + e.getId() + " is already registered. Ignoring event.");
+                } else {
+                    error("Unknown command \"" + e.getCommand() + "\". Ignoring event.");
+                }
+            }
+        } else if (Event.COMMAND_REGISTER.equals(e.getCommand())) {
+            registerDevice(e);
+        } else {
+            Log.e(TAG, "Unknown device id specified. Ignoring event.");
+        }
+    }
+
+    private void registerDevice(Event e) {
+        if (!Event.COMMAND_REGISTER.equals(e.getCommand())) {
+            throw new IllegalStateException(
+                    "Tried to send command \"" + e.getCommand() + "\" to an unregistered device!");
+        }
+        int id = e.getId();
+        Device d = new Device(id, e.getName(), e.getVendorId(), e.getProductId(), e.getBus(),
+                e.getConfiguration(), e.getFfEffectsMax(), e.getAbsInfo());
+        mDevices.append(id, d);
+    }
+
+    private static void error(String msg) {
+        error(msg, null);
+    }
+
+    private static void error(String msg, Exception e) {
+        Log.e(TAG, msg);
+        if (e != null) {
+            Log.e(TAG, Log.getStackTraceString(e));
+        }
+    }
+}
diff --git a/cmds/uinput/uinput b/cmds/uinput/uinput
new file mode 100755
index 0000000..ab2770e
--- /dev/null
+++ b/cmds/uinput/uinput
@@ -0,0 +1,9 @@
+#!/system/bin/sh
+
+# Preload the native portion libuinputcommand_jni.so to bypass the dependency
+# checks in the Java classloader, which prohibit dependencies that aren't
+# listed in system/core/rootdir/etc/public.libraries.android.txt.
+export LD_PRELOAD=libuinputcommand_jni.so
+
+export CLASSPATH=/system/framework/uinput.jar
+exec app_process /system/bin com.android.commands.uinput.Uinput "$@"
diff --git a/config/Android.bp b/config/Android.bp
index 0fb56cb..8dd409b 100644
--- a/config/Android.bp
+++ b/config/Android.bp
@@ -13,6 +13,6 @@
 // limitations under the License.
 
 filegroup {
-    name: "preloaded-classes-blacklist",
-    srcs: ["preloaded-classes-blacklist"],
+    name: "preloaded-classes-denylist",
+    srcs: ["preloaded-classes-denylist"],
 }
diff --git a/config/generate-preloaded-classes.sh b/config/generate-preloaded-classes.sh
index 0ad3a02..b17a366 100755
--- a/config/generate-preloaded-classes.sh
+++ b/config/generate-preloaded-classes.sh
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 if [ "$#" -lt 2 ]; then
-  echo "Usage $0 <input classes file> <blacklist file> [extra classes files]"
+  echo "Usage $0 <input classes file> <denylist file> [extra classes files]"
   exit 1
 fi
 
@@ -31,9 +31,9 @@
 #"
 
 input=$1
-blacklist=$2
+denylist=$2
 shift 2
 extra_classes_files=("$@")
 
 # Disable locale to enable lexicographical sorting
-LC_ALL=C sort "$input" "${extra_classes_files[@]}" | uniq | grep -f "$blacklist" -v -F -x | grep -v "\$NoPreloadHolder"
+LC_ALL=C sort "$input" "${extra_classes_files[@]}" | uniq | grep -f "$denylist" -v -F -x | grep -v "\$NoPreloadHolder"
diff --git a/config/preloaded-classes-blacklist b/config/preloaded-classes-denylist
similarity index 100%
rename from config/preloaded-classes-blacklist
rename to config/preloaded-classes-denylist
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index d67b986..caca05a 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2242,9 +2242,9 @@
      * Resources if one has already been created.
      */
     Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
-            String[] libDirs, int displayId, LoadedApk pkgInfo) {
+            String[] libDirs, LoadedApk pkgInfo) {
         return mResourcesManager.getResources(null, resDir, splitResDirs, overlayDirs, libDirs,
-                displayId, null, pkgInfo.getCompatibilityInfo(), pkgInfo.getClassLoader(), null);
+                null, null, pkgInfo.getCompatibilityInfo(), pkgInfo.getClassLoader(), null);
     }
 
     @UnsupportedAppUsage
@@ -5656,6 +5656,11 @@
             throw new IllegalArgumentException("Activity token not set. Is the activity attached?");
         }
 
+        // WindowConfiguration differences aren't considered as public, check it separately.
+        // multi-window / pip mode changes, if any, should be sent before the configuration
+        // change callback, see also PinnedStackTests#testConfigurationChangeOrderDuringTransition
+        handleWindowingModeChangeIfNeeded(activity, newConfig);
+
         final boolean movedToDifferentDisplay = isDifferentDisplay(activity, displayId);
         boolean shouldReportChange = false;
         if (activity.mCurrentConfig == null) {
@@ -5692,8 +5697,7 @@
         // many places.
         final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
                 amOverrideConfig, contextThemeWrapperOverrideConfig);
-        mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig,
-                displayId, movedToDifferentDisplay);
+        mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId);
 
         activity.mConfigChangeFlags = 0;
         activity.mCurrentConfig = new Configuration(newConfig);
@@ -5709,11 +5713,6 @@
         }
 
         if (shouldReportChange) {
-            // multi-window / pip mode changes, if any, should be sent before the configuration
-            // change callback, see also
-            // PinnedStackTests#testConfigurationChangeOrderDuringTransition
-            handleWindowingModeChangeIfNeeded(activity, newConfig);
-
             activity.mCalled = false;
             activity.onConfigurationChanged(configToReport);
             if (!activity.mCalled) {
@@ -6014,6 +6013,11 @@
             r.mPendingOverrideConfig = null;
         }
 
+        if (displayId == INVALID_DISPLAY) {
+            // If INVALID_DISPLAY is passed assume that the activity should keep its current
+            // display.
+            displayId = r.activity.getDisplayId();
+        }
         final boolean movedToDifferentDisplay = isDifferentDisplay(r.activity, displayId);
         if (r.overrideConfig != null && !r.overrideConfig.isOtherSeqNewer(overrideConfig)
                 && !movedToDifferentDisplay) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 95136bb..7087b60 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -1145,9 +1145,32 @@
     /** @hide */
     public static final int OP_NO_ISOLATED_STORAGE = AppProtoEnums.APP_OP_NO_ISOLATED_STORAGE;
 
+    /**
+     * Phone call is using microphone
+     *
+     * @hide
+     */
+    // TODO: Add as AppProtoEnums
+    public static final int OP_PHONE_CALL_MICROPHONE = 100;
+    /**
+     * Phone call is using camera
+     *
+     * @hide
+     */
+    // TODO: Add as AppProtoEnums
+    public static final int OP_PHONE_CALL_CAMERA = 101;
+
+    /**
+     * Audio is being recorded for hotword detection.
+     *
+     * @hide
+     */
+    // TODO: Add as AppProtoEnums
+    public static final int OP_RECORD_AUDIO_HOTWORD = 102;
+
     /** @hide */
     @UnsupportedAppUsage
-    public static final int _NUM_OP = 100;
+    public static final int _NUM_OP = 103;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1469,6 +1492,26 @@
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
 
+    /**
+     * Phone call is using microphone
+     *
+     * @hide
+     */
+    public static final String OPSTR_PHONE_CALL_MICROPHONE = "android:phone_call_microphone";
+    /**
+     * Phone call is using camera
+     *
+     * @hide
+     */
+    public static final String OPSTR_PHONE_CALL_CAMERA = "android:phone_call_camera";
+
+    /**
+     * Audio is being recorded for hotword detection.
+     *
+     * @hide
+     */
+    public static final String OPSTR_RECORD_AUDIO_HOTWORD = "android:record_audio_hotword";
+
     /** {@link #sAppOpsToNote} not initialized yet for this op */
     private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
     /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
@@ -1658,6 +1701,9 @@
             OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, //AUTO_REVOKE_PERMISSIONS_IF_UNUSED
             OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, //OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
             OP_NO_ISOLATED_STORAGE,             // NO_ISOLATED_STORAGE
+            OP_PHONE_CALL_MICROPHONE,           // OP_PHONE_CALL_MICROPHONE
+            OP_PHONE_CALL_CAMERA,               // OP_PHONE_CALL_CAMERA
+            OP_RECORD_AUDIO_HOTWORD,            // RECORD_AUDIO_HOTWORD
     };
 
     /**
@@ -1764,6 +1810,9 @@
             OPSTR_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
             OPSTR_AUTO_REVOKE_MANAGED_BY_INSTALLER,
             OPSTR_NO_ISOLATED_STORAGE,
+            OPSTR_PHONE_CALL_MICROPHONE,
+            OPSTR_PHONE_CALL_CAMERA,
+            OPSTR_RECORD_AUDIO_HOTWORD,
     };
 
     /**
@@ -1871,6 +1920,9 @@
             "AUTO_REVOKE_PERMISSIONS_IF_UNUSED",
             "AUTO_REVOKE_MANAGED_BY_INSTALLER",
             "NO_ISOLATED_STORAGE",
+            "PHONE_CALL_MICROPHONE",
+            "PHONE_CALL_CAMERA",
+            "RECORD_AUDIO_HOTWORD",
     };
 
     /**
@@ -1979,6 +2031,9 @@
             null, // no permission for OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
             null, // no permission for OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
             null, // no permission for OP_NO_ISOLATED_STORAGE
+            null, // no permission for OP_PHONE_CALL_MICROPHONE
+            null, // no permission for OP_PHONE_CALL_CAMERA
+            null, // no permission for OP_RECORD_AUDIO_HOTWORD
     };
 
     /**
@@ -2087,6 +2142,9 @@
             null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
             null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
             null, // NO_ISOLATED_STORAGE
+            null, // PHONE_CALL_MICROPHONE
+            null, // PHONE_CALL_MICROPHONE
+            null, // RECORD_AUDIO_HOTWORD
     };
 
     /**
@@ -2194,6 +2252,9 @@
             null, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
             null, // AUTO_REVOKE_MANAGED_BY_INSTALLER
             null, // NO_ISOLATED_STORAGE
+            null, // PHONE_CALL_MICROPHONE
+            null, // PHONE_CALL_CAMERA
+            null, // RECORD_AUDIO_HOTWORD
     };
 
     /**
@@ -2300,6 +2361,9 @@
             AppOpsManager.MODE_DEFAULT, // OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED
             AppOpsManager.MODE_ALLOWED, // OP_AUTO_REVOKE_MANAGED_BY_INSTALLER
             AppOpsManager.MODE_ERRORED, // OP_NO_ISOLATED_STORAGE
+            AppOpsManager.MODE_ALLOWED, // PHONE_CALL_MICROPHONE
+            AppOpsManager.MODE_ALLOWED, // PHONE_CALL_CAMERA
+            AppOpsManager.MODE_ALLOWED, // OP_RECORD_AUDIO_HOTWORD
     };
 
     /**
@@ -2410,6 +2474,9 @@
             false, // AUTO_REVOKE_PERMISSIONS_IF_UNUSED
             false, // AUTO_REVOKE_MANAGED_BY_INSTALLER
             true, // NO_ISOLATED_STORAGE
+            false, // PHONE_CALL_MICROPHONE
+            false, // PHONE_CALL_CAMERA
+            false, // RECORD_AUDIO_HOTWORD
     };
 
     /**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 676c6c0..2780036 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -73,6 +73,7 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelableException;
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
@@ -96,7 +97,6 @@
 import android.util.DebugUtils;
 import android.util.LauncherIcons;
 import android.util.Log;
-import android.view.Display;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.Immutable;
@@ -108,7 +108,11 @@
 
 import libcore.util.EmptyArray;
 
+import java.io.IOException;
 import java.lang.ref.WeakReference;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -136,6 +140,12 @@
     // Default flags to use with PackageManager when no flags are given.
     private static final int sDefaultFlags = GET_SHARED_LIBRARY_FILES;
 
+    /** Default set of checksums - includes all available checksums.
+     * @see PackageManager#getChecksums  */
+    private static final int DEFAULT_CHECKSUMS =
+            WHOLE_MERKLE_ROOT_4K_SHA256 | WHOLE_MD5 | WHOLE_SHA1 | WHOLE_SHA256 | WHOLE_SHA512
+                    | PARTIAL_MERKLE_ROOT_1M_SHA256 | PARTIAL_MERKLE_ROOT_1M_SHA512;
+
     // Name of the resource which provides background permission button string
     public static final String APP_PERMISSION_BUTTON_ALLOW_ALWAYS =
             "app_permission_button_allow_always";
@@ -946,6 +956,39 @@
         }
     }
 
+    private static List<byte[]> encodeCertificates(List<Certificate> certs) throws
+            CertificateEncodingException {
+        if (certs == null) {
+            return null;
+        }
+        List<byte[]> result = new ArrayList<>(certs.size());
+        for (Certificate cert : certs) {
+            if (!(cert instanceof X509Certificate)) {
+                throw new CertificateEncodingException("Only X509 certificates supported.");
+            }
+            result.add(cert.getEncoded());
+        }
+        return result;
+    }
+
+    @Override
+    public void getChecksums(@NonNull String packageName, boolean includeSplits,
+            @FileChecksumKind int required, @Nullable List<Certificate> trustedInstallers,
+            @NonNull IntentSender statusReceiver)
+            throws CertificateEncodingException, IOException, NameNotFoundException {
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(statusReceiver);
+        try {
+            mPM.getChecksums(packageName, includeSplits, DEFAULT_CHECKSUMS, required,
+                    encodeCertificates(trustedInstallers), statusReceiver, getUserId());
+        } catch (ParcelableException e) {
+            e.maybeRethrow(PackageManager.NameNotFoundException.class);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Wrap the cached value in a class that does deep compares on string
      * arrays.  The comparison is needed only for the verification mode of
@@ -1748,7 +1791,7 @@
         final Resources r = mContext.mMainThread.getTopLevelResources(
                     sameUid ? app.sourceDir : app.publicSourceDir,
                     sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
-                    app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
+                    app.resourceDirs, app.sharedLibraryFiles,
                     mContext.mPackageInfo);
         if (r != null) {
             return r;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f6b5334..cee607f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -187,6 +187,16 @@
     private static final String XATTR_INODE_CODE_CACHE = "user.inode_code_cache";
 
     /**
+     * Special intent extra that critical system apps can use to hide the notification for a
+     * foreground service. This extra should be placed in the intent passed into {@link
+     * #startForegroundService(Intent)}.
+     *
+     * @hide
+     */
+    private static final String EXTRA_HIDDEN_FOREGROUND_SERVICE =
+            "android.intent.extra.HIDDEN_FOREGROUND_SERVICE";
+
+    /**
      * Map from package name, to preference name, to cached preferences.
      */
     @GuardedBy("ContextImpl.class")
@@ -227,6 +237,15 @@
     private @NonNull Resources mResources;
     private @Nullable Display mDisplay; // may be null if invalid display or not initialized yet.
 
+    /**
+     * If set to {@code true} the resources for this context will be configured for mDisplay which
+     * will override the display configuration inherited from {@link #mToken} (or the global
+     * configuration if mToken is null). Typically set for display contexts and contexts derived
+     * from display contexts where changes to the activity display and the global configuration
+     * display should not impact their resources.
+     */
+    private boolean mForceDisplayOverrideInResources;
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final int mFlags;
 
@@ -1698,9 +1717,12 @@
         try {
             validateServiceIntent(service);
             service.prepareToLeaveProcess(this);
+            final boolean hideForegroundNotification = requireForeground
+                    && service.getBooleanExtra(EXTRA_HIDDEN_FOREGROUND_SERVICE, false);
             ComponentName cn = ActivityManager.getService().startService(
                     mMainThread.getApplicationThread(), service,
                     service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
+                    hideForegroundNotification,
                     getOpPackageName(), getAttributionTag(), user.getIdentifier());
             if (cn != null) {
                 if (cn.getPackageName().equals("!")) {
@@ -2246,8 +2268,8 @@
     }
 
     private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
-            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo,
-            List<ResourcesLoader> resourcesLoader) {
+            @Nullable Integer overrideDisplayId, Configuration overrideConfig,
+            CompatibilityInfo compatInfo, List<ResourcesLoader> resourcesLoader) {
         final String[] splitResDirs;
         final ClassLoader classLoader;
         try {
@@ -2261,7 +2283,7 @@
                 splitResDirs,
                 pi.getOverlayDirs(),
                 pi.getApplicationInfo().sharedLibraryFiles,
-                displayId,
+                overrideDisplayId,
                 overrideConfig,
                 compatInfo,
                 classLoader,
@@ -2278,8 +2300,10 @@
                     new UserHandle(UserHandle.getUserId(application.uid)), flags, null, null);
 
             final int displayId = getDisplayId();
+            final Integer overrideDisplayId = mForceDisplayOverrideInResources
+                    ? displayId : null;
 
-            c.setResources(createResources(mToken, pi, null, displayId, null,
+            c.setResources(createResources(mToken, pi, null, overrideDisplayId, null,
                     getDisplayAdjustments(displayId).getCompatibilityInfo(), null));
             if (c.mResources != null) {
                 return c;
@@ -2313,8 +2337,10 @@
                     mToken, user, flags, null, null);
 
             final int displayId = getDisplayId();
+            final Integer overrideDisplayId = mForceDisplayOverrideInResources
+                    ? displayId : null;
 
-            c.setResources(createResources(mToken, pi, null, displayId, null,
+            c.setResources(createResources(mToken, pi, null, overrideDisplayId, null,
                     getDisplayAdjustments(displayId).getCompatibilityInfo(), null));
             if (c.mResources != null) {
                 return c;
@@ -2348,15 +2374,13 @@
         final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo,
                 mAttributionTag, splitName, mToken, mUser, mFlags, classLoader, null);
 
-        final int displayId = getDisplayId();
-
         context.setResources(ResourcesManager.getInstance().getResources(
                 mToken,
                 mPackageInfo.getResDir(),
                 paths,
                 mPackageInfo.getOverlayDirs(),
                 mPackageInfo.getApplicationInfo().sharedLibraryFiles,
-                displayId,
+                mForceDisplayOverrideInResources ? getDisplayId() : null,
                 null,
                 mPackageInfo.getCompatibilityInfo(),
                 classLoader,
@@ -2370,12 +2394,23 @@
             throw new IllegalArgumentException("overrideConfiguration must not be null");
         }
 
+        if (mForceDisplayOverrideInResources) {
+            // Ensure the resources display metrics are adjusted to match the display this context
+            // is based on.
+            Configuration displayAdjustedConfig = new Configuration();
+            displayAdjustedConfig.setTo(mDisplay.getDisplayAdjustments().getConfiguration(),
+                    ActivityInfo.CONFIG_WINDOW_CONFIGURATION, 1);
+            displayAdjustedConfig.updateFrom(overrideConfiguration);
+            overrideConfiguration = displayAdjustedConfig;
+        }
+
         ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
                 mSplitName, mToken, mUser, mFlags, mClassLoader, null);
 
         final int displayId = getDisplayId();
-
-        context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId,
+        final Integer overrideDisplayId = mForceDisplayOverrideInResources
+                ? displayId : null;
+        context.setResources(createResources(mToken, mPackageInfo, mSplitName, overrideDisplayId,
                 overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
                 mResources.getLoaders()));
         context.mIsUiContext = isUiContext() || isOuterUiContext();
@@ -2393,11 +2428,20 @@
 
         final int displayId = display.getDisplayId();
 
+        // Ensure the resources display metrics are adjusted to match the provided display.
+        Configuration overrideConfig = new Configuration();
+        overrideConfig.setTo(display.getDisplayAdjustments().getConfiguration(),
+                ActivityInfo.CONFIG_WINDOW_CONFIGURATION, 1);
+
         context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId,
-                null, getDisplayAdjustments(displayId).getCompatibilityInfo(),
+                overrideConfig, display.getDisplayAdjustments().getCompatibilityInfo(),
                 mResources.getLoaders()));
         context.mDisplay = display;
         context.mIsAssociatedWithDisplay = true;
+        // Display contexts and any context derived from a display context should always override
+        // the display that would otherwise be inherited from mToken (or the global configuration if
+        // mToken is null).
+        context.mForceDisplayOverrideInResources = true;
         return context;
     }
 
@@ -2415,8 +2459,10 @@
         ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
                 mSplitName, token, mUser, mFlags, mClassLoader, null);
         context.mIsUiContext = true;
-
         context.mIsAssociatedWithDisplay = true;
+        // Window contexts receive configurations directly from the server and as such do not
+        // need to override their display in ResourcesManager.
+        context.mForceDisplayOverrideInResources = false;
         return context;
     }
 
@@ -2759,6 +2805,7 @@
             mDisplay = container.mDisplay;
             mIsAssociatedWithDisplay = container.mIsAssociatedWithDisplay;
             mIsSystemOrSystemUiContext = container.mIsSystemOrSystemUiContext;
+            mForceDisplayOverrideInResources = container.mForceDisplayOverrideInResources;
         } else {
             mBasePackageName = packageInfo.mPackageName;
             ApplicationInfo ainfo = packageInfo.getApplicationInfo();
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index f37ca61b..0a47248 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -156,7 +156,8 @@
     boolean refContentProvider(in IBinder connection, int stableDelta, int unstableDelta);
     PendingIntent getRunningServiceControlPanel(in ComponentName service);
     ComponentName startService(in IApplicationThread caller, in Intent service,
-            in String resolvedType, boolean requireForeground, in String callingPackage,
+            in String resolvedType, boolean requireForeground,
+            boolean hideForegroundNotification, in String callingPackage,
             in String callingFeatureId, int userId);
     @UnsupportedAppUsage
     int stopService(in IApplicationThread caller, in Intent service,
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index aa6a08b..202b615 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -56,7 +56,6 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.view.Display;
 import android.view.DisplayAdjustments;
 
 import com.android.internal.util.ArrayUtils;
@@ -367,7 +366,7 @@
 
                 mResources = ResourcesManager.getInstance().getResources(null, mResDir,
                         splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
-                        Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
+                        null, null, getCompatibilityInfo(),
                         getClassLoader(), mApplication == null ? null
                                 : mApplication.getResources().getLoaders());
             }
@@ -1231,7 +1230,7 @@
 
             mResources = ResourcesManager.getInstance().getResources(null, mResDir,
                     splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
-                    Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
+                    null, null, getCompatibilityInfo(),
                     getClassLoader(), null);
         }
         return mResources;
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index fb2120e..9e4ab33 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -17,6 +17,8 @@
 package android.app;
 
 import static android.app.ActivityThread.DEBUG_CONFIGURATION;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -52,6 +54,7 @@
 import java.io.PrintWriter;
 import java.lang.ref.Reference;
 import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -61,6 +64,7 @@
 import java.util.Objects;
 import java.util.WeakHashMap;
 import java.util.function.Consumer;
+import java.util.function.Function;
 
 /** @hide */
 public class ResourcesManager {
@@ -82,6 +86,12 @@
     private final Configuration mResConfiguration = new Configuration();
 
     /**
+     * The display upon which all Resources are based. Activity, window token, and display context
+     * resources apply their overrides to this display id.
+     */
+    private int mResDisplayId = DEFAULT_DISPLAY;
+
+    /**
      * A mapping of ResourceImpls and their configurations. These are heavy weight objects
      * which should be reused as much as possible.
      */
@@ -155,20 +165,79 @@
     private final ArrayMap<ApkKey, WeakReference<ApkAssets>> mCachedApkAssets = new ArrayMap<>();
 
     /**
-     * Resources and base configuration override associated with an Activity.
+     * Class containing the base configuration override and set of resources associated with an
+     * Activity or {@link WindowContext}.
      */
     private static class ActivityResources {
-        @UnsupportedAppUsage
-        private ActivityResources() {
-        }
+        /**
+         * Override config to apply to all resources associated with the token this instance is
+         * based on.
+         *
+         * @see #activityResources
+         * @see #getResources(IBinder, String, String[], String[], String[], Integer, Configuration,
+         * CompatibilityInfo, ClassLoader, List)
+         */
         public final Configuration overrideConfig = new Configuration();
-        public final ArrayList<WeakReference<Resources>> activityResources = new ArrayList<>();
-        final ReferenceQueue<Resources> activityResourcesQueue = new ReferenceQueue<>();
+
+        /**
+         * The display to apply to all resources associated with the token this instance is based
+         * on.
+         */
+        public int overrideDisplayId;
+
+        /** List of {@link ActivityResource} associated with the token this instance is based on. */
+        public final ArrayList<ActivityResource> activityResources = new ArrayList<>();
+
+        public final ReferenceQueue<Resources> activityResourcesQueue = new ReferenceQueue<>();
+
+        @UnsupportedAppUsage
+        private ActivityResources() {}
+
+        /** Returns the number of live resource references within {@code activityResources}. */
+        public int countLiveReferences() {
+            int count = 0;
+            for (int i = 0; i < activityResources.size(); i++) {
+                WeakReference<Resources> resources = activityResources.get(i).resources;
+                if (resources != null && resources.get() != null) {
+                    count++;
+                }
+            }
+            return count;
+        }
     }
 
     /**
-     * Each Activity may has a base override configuration that is applied to each Resources object,
-     * which in turn may have their own override configuration specified.
+     * Contains a resource derived from an {@link Activity} or {@link WindowContext} and information
+     * about how this resource expects its configuration to differ from the token's.
+     *
+     * @see ActivityResources
+     */
+    // TODO: Ideally this class should be called something token related, like TokenBasedResource.
+    private static class ActivityResource {
+        /**
+         * The override configuration applied on top of the token's override config for this
+         * resource.
+         */
+        public final Configuration overrideConfig = new Configuration();
+
+        /**
+         * If non-null this resource expects its configuration to override the display from the
+         * token's configuration.
+         *
+         * @see #applyDisplayMetricsToConfiguration(DisplayMetrics, Configuration)
+         */
+        @Nullable
+        public Integer overrideDisplayId;
+
+        @Nullable
+        public WeakReference<Resources> resources;
+
+        private ActivityResource() {}
+    }
+
+    /**
+     * Each Activity or WindowToken may has a base override configuration that is applied to each
+     * Resources object, which in turn may have their own override configuration specified.
      */
     @UnsupportedAppUsage
     private final WeakHashMap<IBinder, ActivityResources> mActivityResourceReferences =
@@ -177,7 +246,7 @@
     /**
      * A cache of DisplayId, DisplayAdjustments to Display.
      */
-    private final ArrayMap<Pair<Integer, DisplayAdjustments>, WeakReference<Display>>
+    private final ArrayMap<Pair<Integer, DisplayAdjustments>, SoftReference<Display>>
             mAdjustedDisplays = new ArrayMap<>();
 
     /**
@@ -241,8 +310,7 @@
 
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     public DisplayMetrics getDisplayMetrics() {
-        return getDisplayMetrics(Display.DEFAULT_DISPLAY,
-                DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+        return getDisplayMetrics(mResDisplayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
     }
 
     /**
@@ -260,8 +328,8 @@
         return dm;
     }
 
-    private static void applyNonDefaultDisplayMetricsToConfiguration(
-            @NonNull DisplayMetrics dm, @NonNull Configuration config) {
+    private static void applyDisplayMetricsToConfiguration(@NonNull DisplayMetrics dm,
+            @NonNull Configuration config) {
         config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
         config.densityDpi = dm.densityDpi;
         config.screenWidthDp = (int) (dm.widthPixels / dm.density);
@@ -306,25 +374,28 @@
                 ? new DisplayAdjustments(displayAdjustments) : new DisplayAdjustments();
         final Pair<Integer, DisplayAdjustments> key =
                 Pair.create(displayId, displayAdjustmentsCopy);
+        SoftReference<Display> sd;
         synchronized (this) {
-            WeakReference<Display> wd = mAdjustedDisplays.get(key);
-            if (wd != null) {
-                final Display display = wd.get();
-                if (display != null) {
-                    return display;
-                }
-            }
-            final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
-            if (dm == null) {
-                // may be null early in system startup
-                return null;
-            }
-            final Display display = dm.getCompatibleDisplay(displayId, key.second);
-            if (display != null) {
-                mAdjustedDisplays.put(key, new WeakReference<>(display));
-            }
-            return display;
+            sd = mAdjustedDisplays.get(key);
         }
+        if (sd != null) {
+            final Display display = sd.get();
+            if (display != null) {
+                return display;
+            }
+        }
+        final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
+        if (dm == null) {
+            // may be null early in system startup
+            return null;
+        }
+        final Display display = dm.getCompatibleDisplay(displayId, key.second);
+        if (display != null) {
+            synchronized (this) {
+                mAdjustedDisplays.put(key, new SoftReference<>(display));
+            }
+        }
+        return display;
     }
 
     /**
@@ -502,7 +573,7 @@
 
             int references = countLiveReferences(mResourceReferences);
             for (ActivityResources activityResources : mActivityResourceReferences.values()) {
-                references += countLiveReferences(activityResources.activityResources);
+                references += activityResources.countLiveReferences();
             }
             pw.println(references);
 
@@ -511,38 +582,36 @@
         }
     }
 
-    private Configuration generateConfig(@NonNull ResourcesKey key, @NonNull DisplayMetrics dm) {
+    private Configuration generateConfig(@NonNull ResourcesKey key) {
         Configuration config;
-        final boolean isDefaultDisplay = (key.mDisplayId == Display.DEFAULT_DISPLAY);
         final boolean hasOverrideConfig = key.hasOverrideConfiguration();
-        if (!isDefaultDisplay || hasOverrideConfig) {
+        if (hasOverrideConfig) {
             config = new Configuration(getConfiguration());
-            if (!isDefaultDisplay) {
-                applyNonDefaultDisplayMetricsToConfiguration(dm, config);
-            }
-            if (hasOverrideConfig) {
-                config.updateFrom(key.mOverrideConfiguration);
-                if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
-            }
+            config.updateFrom(key.mOverrideConfiguration);
+            if (DEBUG) Slog.v(TAG, "Applied overrideConfig=" + key.mOverrideConfiguration);
         } else {
             config = getConfiguration();
         }
         return config;
     }
 
+    private int generateDisplayId(@NonNull ResourcesKey key) {
+        return key.mDisplayId != INVALID_DISPLAY ? key.mDisplayId : mResDisplayId;
+    }
+
     private @Nullable ResourcesImpl createResourcesImpl(@NonNull ResourcesKey key,
             @Nullable ApkAssetsSupplier apkSupplier) {
-        final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
-        daj.setCompatibilityInfo(key.mCompatInfo);
-
         final AssetManager assets = createAssetManager(key, apkSupplier);
         if (assets == null) {
             return null;
         }
 
-        final DisplayMetrics dm = getDisplayMetrics(key.mDisplayId, daj);
-        final Configuration config = generateConfig(key, dm);
-        final ResourcesImpl impl = new ResourcesImpl(assets, dm, config, daj);
+        final DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
+        daj.setCompatibilityInfo(key.mCompatInfo);
+
+        final Configuration config = generateConfig(key);
+        final DisplayMetrics displayMetrics = getDisplayMetrics(generateDisplayId(key), daj);
+        final ResourcesImpl impl = new ResourcesImpl(assets, displayMetrics, config, daj);
 
         if (DEBUG) {
             Slog.d(TAG, "- creating impl=" + impl + " with key: " + key);
@@ -652,8 +721,8 @@
 
         final int size = activityResources.activityResources.size();
         for (int index = 0; index < size; index++) {
-            WeakReference<Resources> ref = activityResources.activityResources.get(index);
-            Resources resources = ref.get();
+            ActivityResource activityResource = activityResources.activityResources.get(index);
+            Resources resources = activityResource.resources.get();
             ResourcesKey key = resources == null ? null : findKeyForResourceImplLocked(
                     resources.getImpl());
 
@@ -667,20 +736,28 @@
         return null;
     }
 
-    private @NonNull Resources createResourcesForActivityLocked(@NonNull IBinder activityToken,
+    @NonNull
+    private Resources createResourcesForActivityLocked(@NonNull IBinder activityToken,
+            @NonNull Configuration initialOverrideConfig, @Nullable Integer overrideDisplayId,
             @NonNull ClassLoader classLoader, @NonNull ResourcesImpl impl,
             @NonNull CompatibilityInfo compatInfo) {
         final ActivityResources activityResources = getOrCreateActivityResourcesStructLocked(
                 activityToken);
         cleanupReferences(activityResources.activityResources,
-                activityResources.activityResourcesQueue);
+                activityResources.activityResourcesQueue,
+                (r) -> r.resources);
 
         Resources resources = compatInfo.needsCompatResources() ? new CompatResources(classLoader)
                 : new Resources(classLoader);
         resources.setImpl(impl);
         resources.setCallbacks(mUpdateCallbacks);
-        activityResources.activityResources.add(
-                new WeakReference<>(resources, activityResources.activityResourcesQueue));
+
+        ActivityResource activityResource = new ActivityResource();
+        activityResource.resources = new WeakReference<>(resources,
+                activityResources.activityResourcesQueue);
+        activityResource.overrideConfig.setTo(initialOverrideConfig);
+        activityResource.overrideDisplayId = overrideDisplayId;
+        activityResources.activityResources.add(activityResource);
         if (DEBUG) {
             Slog.d(TAG, "- creating new ref=" + resources);
             Slog.d(TAG, "- setting ref=" + resources + " with impl=" + impl);
@@ -706,7 +783,7 @@
 
     /**
      * Creates base resources for a binder token. Calls to
-     * {@link #getResources(IBinder, String, String[], String[], String[], int, Configuration,
+     * {@link #getResources(IBinder, String, String[], String[], String[], Integer, Configuration,
      * CompatibilityInfo, ClassLoader, List)} with the same binder token will have their override
      * configurations merged with the one specified here.
      *
@@ -743,7 +820,7 @@
                     overlayDirs,
                     libDirs,
                     displayId,
-                    overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
+                    overrideConfig,
                     compatInfo,
                     loaders == null ? null : loaders.toArray(new ResourcesLoader[0]));
             classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
@@ -759,10 +836,7 @@
             }
 
             // Update any existing Activity Resources references.
-            updateResourcesForActivity(token, overrideConfig, displayId,
-                    false /* movedToDifferentDisplay */);
-
-            rebaseKeyForActivity(token, key);
+            updateResourcesForActivity(token, overrideConfig, displayId);
 
             synchronized (this) {
                 Resources resources = findResourcesForActivityLocked(token, key,
@@ -773,7 +847,9 @@
             }
 
             // Now request an actual Resources object.
-            return createResources(token, key, classLoader, /* apkSupplier */ null);
+            return createResourcesForActivity(token, key,
+                    /* initialOverrideConfig */ Configuration.EMPTY, /* overrideDisplayId */ null,
+                    classLoader, /* apkSupplier */ null);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
@@ -781,39 +857,94 @@
 
     /**
      * Rebases a key's override config on top of the Activity's base override.
+     *
+     * @param activityToken the token the supplied {@code key} is derived from.
+     * @param key the key to rebase
+     * @param overridesActivityDisplay whether this key is overriding the display from the token
      */
-    private void rebaseKeyForActivity(IBinder activityToken, ResourcesKey key) {
+    private void rebaseKeyForActivity(IBinder activityToken, ResourcesKey key,
+            boolean overridesActivityDisplay) {
         synchronized (this) {
             final ActivityResources activityResources =
                     getOrCreateActivityResourcesStructLocked(activityToken);
 
-            // Rebase the key's override config on top of the Activity's base override.
-            if (key.hasOverrideConfiguration()
-                    && !activityResources.overrideConfig.equals(Configuration.EMPTY)) {
-                final Configuration temp = new Configuration(activityResources.overrideConfig);
-                temp.updateFrom(key.mOverrideConfiguration);
-                key.mOverrideConfiguration.setTo(temp);
+            if (key.mDisplayId == INVALID_DISPLAY) {
+                key.mDisplayId = activityResources.overrideDisplayId;
             }
+
+            Configuration config;
+            if (key.hasOverrideConfiguration()) {
+                config = new Configuration(activityResources.overrideConfig);
+                config.updateFrom(key.mOverrideConfiguration);
+            } else {
+                config = activityResources.overrideConfig;
+            }
+
+            if (overridesActivityDisplay
+                    && key.mOverrideConfiguration.windowConfiguration.getAppBounds() == null) {
+                if (!key.hasOverrideConfiguration()) {
+                    // Make a copy to handle the case where the override config is set to defaults.
+                    config = new Configuration(config);
+                }
+
+                // If this key is overriding the display from the token and the key's
+                // window config app bounds is null we need to explicitly override this to
+                // ensure the display adjustments are as expected.
+                config.windowConfiguration.setAppBounds(null);
+            }
+
+            key.mOverrideConfiguration.setTo(config);
         }
     }
 
     /**
+     * Rebases a key's override config with display metrics of the {@code overrideDisplay} paired
+     * with the {code displayAdjustments}.
+     *
+     * @see #applyDisplayMetricsToConfiguration(DisplayMetrics, Configuration)
+     */
+    private void rebaseKeyForDisplay(ResourcesKey key, int overrideDisplay) {
+        final Configuration temp = new Configuration();
+
+        DisplayAdjustments daj = new DisplayAdjustments(key.mOverrideConfiguration);
+        daj.setCompatibilityInfo(key.mCompatInfo);
+
+        final DisplayMetrics dm = getDisplayMetrics(overrideDisplay, daj);
+        applyDisplayMetricsToConfiguration(dm, temp);
+
+        if (key.hasOverrideConfiguration()) {
+            temp.updateFrom(key.mOverrideConfiguration);
+        }
+        key.mOverrideConfiguration.setTo(temp);
+    }
+
+    /**
      * Check WeakReferences and remove any dead references so they don't pile up.
      */
     private static <T> void cleanupReferences(ArrayList<WeakReference<T>> references,
             ReferenceQueue<T> referenceQueue) {
-        Reference<? extends T> enduedRef = referenceQueue.poll();
-        if (enduedRef == null) {
+        cleanupReferences(references, referenceQueue, Function.identity());
+    }
+
+    /**
+     * Check WeakReferences and remove any dead references so they don't pile up.
+     */
+    private static <C, T> void cleanupReferences(ArrayList<C> referenceContainers,
+            ReferenceQueue<T> referenceQueue, Function<C, WeakReference<T>> unwrappingFunction) {
+        Reference<? extends T> enqueuedRef = referenceQueue.poll();
+        if (enqueuedRef == null) {
             return;
         }
 
         final HashSet<Reference<? extends T>> deadReferences = new HashSet<>();
-        for (; enduedRef != null; enduedRef = referenceQueue.poll()) {
-            deadReferences.add(enduedRef);
+        for (; enqueuedRef != null; enqueuedRef = referenceQueue.poll()) {
+            deadReferences.add(enqueuedRef);
         }
 
-        ArrayUtils.unstableRemoveIf(references,
-                (ref) -> ref == null || deadReferences.contains(ref));
+        ArrayUtils.unstableRemoveIf(referenceContainers, (refContainer) -> {
+            WeakReference<T> ref = unwrappingFunction.apply(refContainer);
+            return ref == null || deadReferences.contains(ref);
+        });
     }
 
     /**
@@ -825,8 +956,8 @@
         Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
                 "ResourcesManager#createApkAssetsSupplierNotLocked");
         try {
-            if (Thread.holdsLock(this)) {
-                Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
+            if (DEBUG && Thread.holdsLock(this)) {
+                Slog.w(TAG, "Calling thread " + Thread.currentThread().getName()
                     + " is holding mLock", new Throwable());
             }
 
@@ -849,17 +980,36 @@
     /**
      * Creates a Resources object set with a ResourcesImpl object matching the given key.
      *
-     * @param activityToken The Activity this Resources object should be associated with.
      * @param key The key describing the parameters of the ResourcesImpl object.
      * @param classLoader The classloader to use for the Resources object.
      *                    If null, {@link ClassLoader#getSystemClassLoader()} is used.
-     * @param apkSupplier The apk assets supplier to use when creating a new ResourcesImpl object.
      * @return A Resources object that gets updated when
      *         {@link #applyConfigurationToResourcesLocked(Configuration, CompatibilityInfo)}
      *         is called.
      */
-    private @Nullable Resources createResources(@Nullable IBinder activityToken,
-            @NonNull ResourcesKey key, @NonNull ClassLoader classLoader,
+    @Nullable
+    private Resources createResources(@NonNull ResourcesKey key, @NonNull ClassLoader classLoader,
+            @Nullable ApkAssetsSupplier apkSupplier) {
+        synchronized (this) {
+            if (DEBUG) {
+                Throwable here = new Throwable();
+                here.fillInStackTrace();
+                Slog.w(TAG, "!! Create resources for key=" + key, here);
+            }
+
+            ResourcesImpl resourcesImpl = findOrCreateResourcesImplForKeyLocked(key, apkSupplier);
+            if (resourcesImpl == null) {
+                return null;
+            }
+
+            return createResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
+        }
+    }
+
+    @Nullable
+    private Resources createResourcesForActivity(@NonNull IBinder activityToken,
+            @NonNull ResourcesKey key, @NonNull Configuration initialOverrideConfig,
+            @Nullable Integer overrideDisplayId, @NonNull ClassLoader classLoader,
             @Nullable ApkAssetsSupplier apkSupplier) {
         synchronized (this) {
             if (DEBUG) {
@@ -873,12 +1023,8 @@
                 return null;
             }
 
-            if (activityToken != null) {
-                return createResourcesForActivityLocked(activityToken, classLoader,
-                        resourcesImpl, key.mCompatInfo);
-            } else {
-                return createResourcesLocked(classLoader, resourcesImpl, key.mCompatInfo);
-            }
+            return createResourcesForActivityLocked(activityToken, initialOverrideConfig,
+                    overrideDisplayId, classLoader, resourcesImpl, key.mCompatInfo);
         }
     }
 
@@ -899,7 +1045,10 @@
      * @param splitResDirs An array of split resource paths. Can be null.
      * @param overlayDirs An array of overlay paths. Can be null.
      * @param libDirs An array of resource library paths. Can be null.
-     * @param displayId The ID of the display for which to create the resources.
+     * @param overrideDisplayId The ID of the display for which the returned Resources should be
+     * based. This will cause display-based configuration properties to override those of the base
+     * Resources for the {@code activityToken}, or the global configuration if {@code activityToken}
+     * is null.
      * @param overrideConfig The configuration to apply on top of the base configuration. Can be
      * null. Mostly used with Activities that are in multi-window which may override width and
      * height properties from the base config.
@@ -909,13 +1058,14 @@
      * {@link ClassLoader#getSystemClassLoader()} is used.
      * @return a Resources object from which to access resources.
      */
-    public @Nullable Resources getResources(
+    @Nullable
+    public Resources getResources(
             @Nullable IBinder activityToken,
             @Nullable String resDir,
             @Nullable String[] splitResDirs,
             @Nullable String[] overlayDirs,
             @Nullable String[] libDirs,
-            int displayId,
+            @Nullable Integer overrideDisplayId,
             @Nullable Configuration overrideConfig,
             @NonNull CompatibilityInfo compatInfo,
             @Nullable ClassLoader classLoader,
@@ -927,20 +1077,30 @@
                     splitResDirs,
                     overlayDirs,
                     libDirs,
-                    displayId,
-                    overrideConfig != null ? new Configuration(overrideConfig) : null, // Copy
+                    overrideDisplayId != null ? overrideDisplayId : INVALID_DISPLAY,
+                    overrideConfig,
                     compatInfo,
                     loaders == null ? null : loaders.toArray(new ResourcesLoader[0]));
             classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader();
 
-            if (activityToken != null) {
-                rebaseKeyForActivity(activityToken, key);
-            }
-
             // Preload the ApkAssets required by the key to prevent performing heavy I/O while the
             // ResourcesManager lock is held.
             final ApkAssetsSupplier assetsSupplier = createApkAssetsSupplierNotLocked(key);
-            return createResources(activityToken, key, classLoader, assetsSupplier);
+
+            if (overrideDisplayId != null) {
+                rebaseKeyForDisplay(key, overrideDisplayId);
+            }
+
+            Resources resources;
+            if (activityToken != null) {
+                Configuration initialOverrideConfig = new Configuration(key.mOverrideConfiguration);
+                rebaseKeyForActivity(activityToken, key, overrideDisplayId != null);
+                resources = createResourcesForActivity(activityToken, key, initialOverrideConfig,
+                        overrideDisplayId, classLoader, assetsSupplier);
+            } else {
+                resources = createResources(key, classLoader, assetsSupplier);
+            }
+            return resources;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
         }
@@ -949,24 +1109,26 @@
     /**
      * Updates an Activity's Resources object with overrideConfig. The Resources object
      * that was previously returned by {@link #getResources(IBinder, String, String[], String[],
-     * String[], int, Configuration, CompatibilityInfo, ClassLoader, List)} is still valid and will
-     * have the updated configuration.
+     * String[], Integer, Configuration, CompatibilityInfo, ClassLoader, List)} is still valid and
+     * will have the updated configuration.
      *
      * @param activityToken The Activity token.
      * @param overrideConfig The configuration override to update.
      * @param displayId Id of the display where activity currently resides.
-     * @param movedToDifferentDisplay Indicates if the activity was moved to different display.
      */
     public void updateResourcesForActivity(@NonNull IBinder activityToken,
-            @Nullable Configuration overrideConfig, int displayId,
-            boolean movedToDifferentDisplay) {
+            @Nullable Configuration overrideConfig, int displayId) {
         try {
             Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
                     "ResourcesManager#updateResourcesForActivity");
+            if (displayId == INVALID_DISPLAY) {
+                throw new IllegalArgumentException("displayId can not be INVALID_DISPLAY");
+            }
             synchronized (this) {
                 final ActivityResources activityResources =
                         getOrCreateActivityResourcesStructLocked(activityToken);
 
+                boolean movedToDifferentDisplay = activityResources.overrideDisplayId != displayId;
                 if (Objects.equals(activityResources.overrideConfig, overrideConfig)
                         && !movedToDifferentDisplay) {
                     // They are the same and no change of display id, no work to do.
@@ -983,6 +1145,8 @@
                 } else {
                     activityResources.overrideConfig.unset();
                 }
+                // Update the Activity's override display id.
+                activityResources.overrideDisplayId = displayId;
 
                 if (DEBUG) {
                     Throwable here = new Throwable();
@@ -1000,24 +1164,26 @@
                 // Rebase each Resources associated with this Activity.
                 final int refCount = activityResources.activityResources.size();
                 for (int i = 0; i < refCount; i++) {
-                    final WeakReference<Resources> weakResRef =
+                    final ActivityResource activityResource =
                             activityResources.activityResources.get(i);
 
-                    final Resources resources = weakResRef.get();
+                    final Resources resources = activityResource.resources.get();
                     if (resources == null) {
                         continue;
                     }
 
-                    final ResourcesKey newKey = rebaseActivityOverrideConfig(resources, oldConfig,
+                    final ResourcesKey newKey = rebaseActivityOverrideConfig(activityResource,
                             overrideConfig, displayId);
-                    if (newKey != null) {
-                        final ResourcesImpl resourcesImpl =
-                                findOrCreateResourcesImplForKeyLocked(newKey);
-                        if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
-                            // Set the ResourcesImpl, updating it for all users of this Resources
-                            // object.
-                            resources.setImpl(resourcesImpl);
-                        }
+                    if (newKey == null) {
+                        continue;
+                    }
+
+                    final ResourcesImpl resourcesImpl =
+                            findOrCreateResourcesImplForKeyLocked(newKey);
+                    if (resourcesImpl != null && resourcesImpl != resources.getImpl()) {
+                        // Set the ResourcesImpl, updating it for all users of this Resources
+                        // object.
+                        resources.setImpl(resourcesImpl);
                     }
                 }
             }
@@ -1031,9 +1197,13 @@
      * that an Activity's Resources should be set to.
      */
     @Nullable
-    private ResourcesKey rebaseActivityOverrideConfig(@NonNull Resources resources,
-            @NonNull Configuration oldOverrideConfig, @Nullable Configuration newOverrideConfig,
-            int displayId) {
+    private ResourcesKey rebaseActivityOverrideConfig(@NonNull ActivityResource activityResource,
+            @Nullable Configuration newOverrideConfig, int displayId) {
+        final Resources resources = activityResource.resources.get();
+        if (resources == null) {
+            return null;
+        }
+
         // Extract the ResourcesKey that was last used to create the Resources for this
         // activity.
         final ResourcesKey oldKey = findKeyForResourceImplLocked(resources.getImpl());
@@ -1049,16 +1219,33 @@
             rebasedOverrideConfig.setTo(newOverrideConfig);
         }
 
-        final boolean hadOverrideConfig = !oldOverrideConfig.equals(Configuration.EMPTY);
-        if (hadOverrideConfig && oldKey.hasOverrideConfiguration()) {
-            // Generate a delta between the old base Activity override configuration and
-            // the actual final override configuration that was used to figure out the
-            // real delta this Resources object wanted.
-            Configuration overrideOverrideConfig = Configuration.generateDelta(
-                    oldOverrideConfig, oldKey.mOverrideConfiguration);
-            rebasedOverrideConfig.updateFrom(overrideOverrideConfig);
+        final Integer overrideDisplayId = activityResource.overrideDisplayId;
+        if (overrideDisplayId != null) {
+            DisplayAdjustments displayAdjustments = new DisplayAdjustments(rebasedOverrideConfig);
+            displayAdjustments.getConfiguration().setTo(activityResource.overrideConfig);
+            displayAdjustments.setCompatibilityInfo(oldKey.mCompatInfo);
+
+            DisplayMetrics dm = getDisplayMetrics(overrideDisplayId, displayAdjustments);
+            applyDisplayMetricsToConfiguration(dm, rebasedOverrideConfig);
         }
 
+        final boolean hasOverrideConfig =
+                !activityResource.overrideConfig.equals(Configuration.EMPTY);
+        if (hasOverrideConfig) {
+            rebasedOverrideConfig.updateFrom(activityResource.overrideConfig);
+        }
+
+        if (activityResource.overrideDisplayId != null
+                && activityResource.overrideConfig.windowConfiguration.getAppBounds() == null) {
+            // If this activity resource is overriding the display from the token and the key's
+            // window config app bounds is null we need to explicitly override this to
+            // ensure the display adjustments are as expected.
+            rebasedOverrideConfig.windowConfiguration.setAppBounds(null);
+        }
+
+        // Ensure the new key keeps the expected override display instead of the new token display.
+        displayId = overrideDisplayId != null ? overrideDisplayId : displayId;
+
         // Create the new ResourcesKey with the rebased override config.
         final ResourcesKey newKey = new ResourcesKey(oldKey.mResDir,
                 oldKey.mSplitResDirs, oldKey.mOverlayDirs, oldKey.mLibDirs,
@@ -1090,12 +1277,11 @@
                         + mResConfiguration.seq + ", newSeq=" + config.seq);
                 return false;
             }
-            int changes = mResConfiguration.updateFrom(config);
+
             // Things might have changed in display manager, so clear the cached displays.
             mAdjustedDisplays.clear();
 
-            DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
-
+            int changes = mResConfiguration.updateFrom(config);
             if (compat != null && (mResCompatibilityInfo == null ||
                     !mResCompatibilityInfo.equals(compat))) {
                 mResCompatibilityInfo = compat;
@@ -1104,10 +1290,10 @@
                         | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
             }
 
-            Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
+            DisplayMetrics displayMetrics = getDisplayMetrics();
+            Resources.updateSystemConfiguration(config, displayMetrics, compat);
 
             ApplicationPackageManager.configurationChanged();
-            //Slog.i(TAG, "Configuration changed in " + currentPackageName());
 
             Configuration tmpConfig = new Configuration();
 
@@ -1137,11 +1323,7 @@
         }
 
         tmpConfig.setTo(config);
-
-        // Apply the override configuration before setting the display adjustments to ensure that
-        // the process config does not override activity display adjustments.
-        final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
-        if (hasOverrideConfiguration) {
+        if (key.hasOverrideConfiguration()) {
             tmpConfig.updateFrom(key.mOverrideConfiguration);
         }
 
@@ -1153,22 +1335,8 @@
             daj = new DisplayAdjustments(daj);
             daj.setCompatibilityInfo(compat);
         }
-
-        final int displayId = key.mDisplayId;
-        if (displayId == Display.DEFAULT_DISPLAY) {
-            daj.setConfiguration(tmpConfig);
-        }
-        DisplayMetrics dm = getDisplayMetrics(displayId, daj);
-        if (displayId != Display.DEFAULT_DISPLAY) {
-            applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
-
-            // Re-apply the override configuration to ensure that configuration contexts based on
-            // a display context (ex: createDisplayContext().createConfigurationContext()) have the
-            // correct override.
-            if (hasOverrideConfiguration) {
-                tmpConfig.updateFrom(key.mOverrideConfiguration);
-            }
-        }
+        daj.setConfiguration(tmpConfig);
+        DisplayMetrics dm = getDisplayMetrics(generateDisplayId(key), daj);
 
         resourcesImpl.updateConfiguration(tmpConfig, dm, compat);
     }
@@ -1305,8 +1473,10 @@
         for (ActivityResources activityResources : mActivityResourceReferences.values()) {
             final int resCount = activityResources.activityResources.size();
             for (int i = 0; i < resCount; i++) {
-                final WeakReference<Resources> ref = activityResources.activityResources.get(i);
-                final Resources r = ref != null ? ref.get() : null;
+                final ActivityResource activityResource =
+                        activityResources.activityResources.get(i);
+                final Resources r = activityResource != null
+                        ? activityResource.resources.get() : null;
                 if (r != null) {
                     final ResourcesKey key = updatedResourceKeys.get(r.getImpl());
                     if (key != null) {
@@ -1338,10 +1508,16 @@
             if (tokenResources == null) {
                 return false;
             }
-            final ArrayList<WeakReference<Resources>> resourcesRefs =
-                    tokenResources.activityResources;
+            final ArrayList<ActivityResource> resourcesRefs = tokenResources.activityResources;
             for (int i = resourcesRefs.size() - 1; i >= 0; i--) {
-                final Resources res = resourcesRefs.get(i).get();
+                final ActivityResource activityResource = resourcesRefs.get(i);
+                if (activityResource.overrideDisplayId != null) {
+                    // This resource overrides the display of the token so we should not be
+                    // modifying its display adjustments here.
+                    continue;
+                }
+
+                final Resources res = activityResource.resources.get();
                 if (res != null) {
                     res.overrideDisplayAdjustments(override);
                     handled = true;
diff --git a/core/java/android/app/WindowTokenClient.java b/core/java/android/app/WindowTokenClient.java
index b5d1039..9092ef36 100644
--- a/core/java/android/app/WindowTokenClient.java
+++ b/core/java/android/app/WindowTokenClient.java
@@ -71,8 +71,7 @@
         final boolean configChanged = config.diff(newConfig) != 0;
         if (displayChanged || configChanged) {
             // TODO(ag/9789103): update resource manager logic to track non-activity tokens
-            mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId,
-                    displayChanged);
+            mResourcesManager.updateResourcesForActivity(this, newConfig, newDisplayId);
         }
         if (displayChanged) {
             context.updateDisplay(newDisplayId);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index c61426d..98de85d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9751,21 +9751,6 @@
     }
 
     /**
-     * @hide
-     * Return if this user is a system-only user. An admin can manage a device from a system only
-     * user by calling {@link #ACTION_PROVISION_MANAGED_SHAREABLE_DEVICE}.
-     * @param admin Which device owner this request is associated with.
-     * @return if this user is a system-only user.
-     */
-    public boolean isSystemOnlyUser(@NonNull ComponentName admin) {
-        try {
-            return mService.isSystemOnlyUser(admin);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Called by device owner, or profile owner on organization-owned device, to get the MAC
      * address of the Wi-Fi device.
      *
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 9c6a274..1c7b617 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -345,7 +345,6 @@
     void setKeepUninstalledPackages(in ComponentName admin, in String callerPackage, in List<String> packageList);
     List<String> getKeepUninstalledPackages(in ComponentName admin, in String callerPackage);
     boolean isManagedProfile(in ComponentName admin);
-    boolean isSystemOnlyUser(in ComponentName admin);
     String getWifiMacAddress(in ComponentName admin);
     void reboot(in ComponentName admin);
 
diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java
index 7f43640..fa135b1 100644
--- a/core/java/android/app/prediction/AppPredictor.java
+++ b/core/java/android/app/prediction/AppPredictor.java
@@ -83,6 +83,8 @@
     private final AppPredictionSessionId mSessionId;
     private final ArrayMap<Callback, CallbackWrapper> mRegisteredCallbacks = new ArrayMap<>();
 
+    private final IBinder mToken = new Binder();
+
     /**
      * Creates a new Prediction client.
      * <p>
@@ -98,7 +100,7 @@
         mSessionId = new AppPredictionSessionId(
                 context.getPackageName() + ":" + UUID.randomUUID().toString(), context.getUserId());
         try {
-            mPredictionManager.createPredictionSession(predictionContext, mSessionId);
+            mPredictionManager.createPredictionSession(predictionContext, mSessionId, mToken);
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to create predictor", e);
             e.rethrowAsRuntimeException();
diff --git a/core/java/android/app/prediction/IPredictionManager.aidl b/core/java/android/app/prediction/IPredictionManager.aidl
index 587e3fd..863fc6f9 100644
--- a/core/java/android/app/prediction/IPredictionManager.aidl
+++ b/core/java/android/app/prediction/IPredictionManager.aidl
@@ -29,7 +29,7 @@
 interface IPredictionManager {
 
     void createPredictionSession(in AppPredictionContext context,
-            in AppPredictionSessionId sessionId);
+            in AppPredictionSessionId sessionId, in IBinder token);
 
     void notifyAppTargetEvent(in AppPredictionSessionId sessionId, in AppTargetEvent event);
 
diff --git a/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl b/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl
index af77fe0..6d0fe72 100644
--- a/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl
+++ b/core/java/android/app/timezonedetector/ITimeZoneConfigurationListener.aidl
@@ -20,5 +20,5 @@
 
 /** {@hide} */
 oneway interface ITimeZoneConfigurationListener {
-    void onChange(in TimeZoneConfiguration configuration);
+    void onChange();
 }
\ No newline at end of file
diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
index 6e93af6..4f7e1f6 100644
--- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
+++ b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
@@ -32,17 +32,15 @@
  * this Binder interface directly. See {@link android.app.timezonedetector.TimeZoneDetectorService}
  * for more complete documentation.
  *
- *
  * {@hide}
  */
 interface ITimeZoneDetectorService {
   TimeZoneCapabilities getCapabilities();
-
-  TimeZoneConfiguration getConfiguration();
-  boolean updateConfiguration(in TimeZoneConfiguration configuration);
   void addConfigurationListener(ITimeZoneConfigurationListener listener);
   void removeConfigurationListener(ITimeZoneConfigurationListener listener);
 
+  boolean updateConfiguration(in TimeZoneConfiguration configuration);
+
   boolean suggestManualTimeZone(in ManualTimeZoneSuggestion timeZoneSuggestion);
   void suggestTelephonyTimeZone(in TelephonyTimeZoneSuggestion timeZoneSuggestion);
 }
diff --git a/core/java/android/app/timezonedetector/TEST_MAPPING b/core/java/android/app/timezonedetector/TEST_MAPPING
new file mode 100644
index 0000000..46f2319
--- /dev/null
+++ b/core/java/android/app/timezonedetector/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "android.app.timezonedetector."
+        }
+      ]
+    }
+  ]
+}
diff --git a/core/java/android/app/timezonedetector/TimeZoneCapabilities.java b/core/java/android/app/timezonedetector/TimeZoneCapabilities.java
index cc0af3f..09fffe9 100644
--- a/core/java/android/app/timezonedetector/TimeZoneCapabilities.java
+++ b/core/java/android/app/timezonedetector/TimeZoneCapabilities.java
@@ -16,9 +16,12 @@
 
 package android.app.timezonedetector;
 
+import static android.app.timezonedetector.TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED;
+import static android.app.timezonedetector.TimeZoneConfiguration.SETTING_GEO_DETECTION_ENABLED;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
-import android.annotation.UserIdInt;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -38,9 +41,9 @@
  *
  * <p>Actions have associated methods, see the documentation for each action for details.
  *
- * <p>For configuration capabilities, the associated current configuration value can be retrieved
- * using {@link TimeZoneDetector#getConfiguration()} and may be changed using
- * {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)}.
+ * <p>For configuration settings capabilities, the associated settings value can be found via
+ * {@link #getConfiguration()} and may be changed using {@link
+ * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} (if the user's capabilities allow).
  *
  * <p>Note: Capabilities are independent of app permissions required to call the associated APIs.
  *
@@ -60,7 +63,8 @@
     public static final int CAPABILITY_NOT_SUPPORTED = 10;
 
     /**
-     * Indicates that a capability is supported on this device, but not allowed for the user.
+     * Indicates that a capability is supported on this device, but not allowed for the user, e.g.
+     * if the capability relates to the ability to modify settings the user is not able to.
      * This could be because of the user's type (e.g. maybe it applies to the primary user only) or
      * device policy. Depending on the capability, this could mean the associated UI
      * should be hidden, or displayed but disabled.
@@ -68,9 +72,11 @@
     public static final int CAPABILITY_NOT_ALLOWED = 20;
 
     /**
-     * Indicates that a capability is possessed but not applicable, e.g. if it is configuration,
-     * the current configuration or device state renders it irrelevant. The associated UI may be
-     * hidden, disabled, or left visible (but ineffective) depending on requirements.
+     * Indicates that a capability is possessed but not currently applicable, e.g. if the
+     * capability relates to the ability to modify settings, the user has the ability to modify
+     * it, but it is currently rendered irrelevant by other settings or other device state (flags,
+     * resource config, etc.). The associated UI may be hidden, disabled, or left visible (but
+     * ineffective) depending on requirements.
      */
     public static final int CAPABILITY_NOT_APPLICABLE = 30;
 
@@ -89,13 +95,13 @@
             };
 
 
-    private final @UserIdInt int mUserId;
+    @NonNull private final TimeZoneConfiguration mConfiguration;
     private final @CapabilityState int mConfigureAutoDetectionEnabled;
     private final @CapabilityState int mConfigureGeoDetectionEnabled;
     private final @CapabilityState int mSuggestManualTimeZone;
 
     private TimeZoneCapabilities(@NonNull Builder builder) {
-        this.mUserId = builder.mUserId;
+        this.mConfiguration = Objects.requireNonNull(builder.mConfiguration);
         this.mConfigureAutoDetectionEnabled = builder.mConfigureAutoDetectionEnabled;
         this.mConfigureGeoDetectionEnabled = builder.mConfigureGeoDetectionEnabled;
         this.mSuggestManualTimeZone = builder.mSuggestManualTimeZone;
@@ -103,7 +109,8 @@
 
     @NonNull
     private static TimeZoneCapabilities createFromParcel(Parcel in) {
-        return new TimeZoneCapabilities.Builder(in.readInt())
+        return new TimeZoneCapabilities.Builder()
+                .setConfiguration(in.readParcelable(null))
                 .setConfigureAutoDetectionEnabled(in.readInt())
                 .setConfigureGeoDetectionEnabled(in.readInt())
                 .setSuggestManualTimeZone(in.readInt())
@@ -112,21 +119,24 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mUserId);
+        dest.writeParcelable(mConfiguration, flags);
         dest.writeInt(mConfigureAutoDetectionEnabled);
         dest.writeInt(mConfigureGeoDetectionEnabled);
         dest.writeInt(mSuggestManualTimeZone);
     }
 
-    /** Returns the user ID the capabilities are for. */
-    public @UserIdInt int getUserId() {
-        return mUserId;
+    /**
+     * Returns the user's time zone behavior configuration.
+     */
+    public @NonNull TimeZoneConfiguration getConfiguration() {
+        return mConfiguration;
     }
 
     /**
-     * Returns the user's capability state for controlling whether automatic time zone detection is
-     * enabled via {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link
-     * TimeZoneConfiguration#isAutoDetectionEnabled()}.
+     * Returns the capability state associated with the user's ability to modify the automatic time
+     * zone detection setting. The setting can be updated via {@link
+     * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and accessed via {@link
+     * #getConfiguration()}.
      */
     @CapabilityState
     public int getConfigureAutoDetectionEnabled() {
@@ -134,9 +144,10 @@
     }
 
     /**
-     * Returns the user's capability state for controlling whether geolocation can be used to detect
-     * time zone via {@link TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and {@link
-     * TimeZoneConfiguration#isGeoDetectionEnabled()}.
+     * Returns the capability state associated with the user's ability to modify the geolocation
+     * detection setting. The setting can be updated via {@link
+     * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and accessed via {@link
+     * #getConfiguration()}.
      */
     @CapabilityState
     public int getConfigureGeoDetectionEnabled() {
@@ -144,8 +155,8 @@
     }
 
     /**
-     * Returns the user's capability state for manually setting the time zone on a device via
-     * {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}.
+     * Returns the capability state associated with the user's ability to manually set the time zone
+     * on a device via {@link TimeZoneDetector#suggestManualTimeZone(ManualTimeZoneSuggestion)}.
      *
      * <p>The suggestion will be ignored in all cases unless the value is {@link
      * #CAPABILITY_POSSESSED}. See also {@link TimeZoneConfiguration#isAutoDetectionEnabled()}.
@@ -155,6 +166,38 @@
         return mSuggestManualTimeZone;
     }
 
+    /**
+     * Constructs a new {@link TimeZoneConfiguration} from an {@code oldConfiguration} and a set of
+     * {@code requestedChanges}, if the current capabilities allow. The new configuration is
+     * returned and the capabilities are left unchanged. If the capabilities do not permit one or
+     * more of the changes then {@code null} is returned.
+     */
+    @Nullable
+    public TimeZoneConfiguration applyUpdate(TimeZoneConfiguration requestedChanges) {
+        if (requestedChanges.getUserId() != mConfiguration.getUserId()) {
+            throw new IllegalArgumentException("User does not match:"
+                    + " this=" + mConfiguration + ", other=" + requestedChanges);
+        }
+
+        TimeZoneConfiguration.Builder newConfigBuilder =
+                new TimeZoneConfiguration.Builder(mConfiguration);
+        if (requestedChanges.hasSetting(SETTING_AUTO_DETECTION_ENABLED)) {
+            if (getConfigureAutoDetectionEnabled() < CAPABILITY_NOT_APPLICABLE) {
+                return null;
+            }
+            newConfigBuilder.setAutoDetectionEnabled(requestedChanges.isAutoDetectionEnabled());
+        }
+
+        if (requestedChanges.hasSetting(SETTING_GEO_DETECTION_ENABLED)) {
+            if (getConfigureGeoDetectionEnabled() < CAPABILITY_NOT_APPLICABLE) {
+                return null;
+            }
+            newConfigBuilder.setGeoDetectionEnabled(requestedChanges.isGeoDetectionEnabled());
+        }
+
+        return newConfigBuilder.build();
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -169,7 +212,7 @@
             return false;
         }
         TimeZoneCapabilities that = (TimeZoneCapabilities) o;
-        return mUserId == that.mUserId
+        return Objects.equals(mConfiguration, that.mConfiguration)
                 && mConfigureAutoDetectionEnabled == that.mConfigureAutoDetectionEnabled
                 && mConfigureGeoDetectionEnabled == that.mConfigureGeoDetectionEnabled
                 && mSuggestManualTimeZone == that.mSuggestManualTimeZone;
@@ -177,7 +220,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mUserId,
+        return Objects.hash(mConfiguration,
                 mConfigureAutoDetectionEnabled,
                 mConfigureGeoDetectionEnabled,
                 mSuggestManualTimeZone);
@@ -186,7 +229,7 @@
     @Override
     public String toString() {
         return "TimeZoneDetectorCapabilities{"
-                + "mUserId=" + mUserId
+                + "mConfiguration=" + mConfiguration
                 + ", mConfigureAutomaticDetectionEnabled=" + mConfigureAutoDetectionEnabled
                 + ", mConfigureGeoDetectionEnabled=" + mConfigureGeoDetectionEnabled
                 + ", mSuggestManualTimeZone=" + mSuggestManualTimeZone
@@ -196,16 +239,18 @@
     /** @hide */
     public static class Builder {
 
-        private final @UserIdInt int mUserId;
+        private TimeZoneConfiguration mConfiguration;
         private @CapabilityState int mConfigureAutoDetectionEnabled;
         private @CapabilityState int mConfigureGeoDetectionEnabled;
         private @CapabilityState int mSuggestManualTimeZone;
 
-        /**
-         * Creates a new Builder with no properties set.
-         */
-        public Builder(@UserIdInt int userId) {
-            mUserId = userId;
+        /** Sets the user-visible configuration settings. */
+        public Builder setConfiguration(@NonNull TimeZoneConfiguration configuration) {
+            if (!configuration.isComplete()) {
+                throw new IllegalArgumentException(configuration + " is not complete");
+            }
+            this.mConfiguration = configuration;
+            return this;
         }
 
         /** Sets the state for the automatic time zone detection enabled config. */
diff --git a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java b/core/java/android/app/timezonedetector/TimeZoneConfiguration.java
index 6f84ee2..95db0a2 100644
--- a/core/java/android/app/timezonedetector/TimeZoneConfiguration.java
+++ b/core/java/android/app/timezonedetector/TimeZoneConfiguration.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.StringDef;
+import android.annotation.UserIdInt;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -27,21 +28,20 @@
 import java.util.Objects;
 
 /**
- * Configuration that controls the behavior of the time zone detector associated with a specific
- * user.
+ * User visible settings that control the behavior of the time zone detector / manual time zone
+ * entry.
  *
- * <p>Configuration consists of a set of known properties. When reading configuration via
- * {@link TimeZoneDetector#getConfiguration()} values for all known properties will be provided. In
- * some cases, such as when the configuration relies on optional hardware, the values may be
- * meaningless / defaulted to safe values.
+ * <p>When reading the configuration, values for all settings will be provided. In some cases, such
+ * as when the device behavior relies on optional hardware / OEM configuration, or the value of
+ * several settings, the device behavior may not be directly affected by the setting value.
  *
- * <p>Configuration properties can be left absent when updating configuration via {@link
- * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and those values will not be
- * changed. Not all configuration properties can be modified by all users. See {@link
- * TimeZoneDetector#getCapabilities()} and {@link TimeZoneCapabilities}.
+ * <p>Settings can be left absent when updating configuration via {@link
+ * TimeZoneDetector#updateConfiguration(TimeZoneConfiguration)} and those settings will not be
+ * changed. Not all configuration settings can be modified by all users: see {@link
+ * TimeZoneDetector#getCapabilities()} and {@link TimeZoneCapabilities} for details.
  *
- * <p>See {@link #isComplete()} to tell if all known properties are present, and {@link
- * #hasProperty(String)} with {@code PROPERTY_} constants for testing individual properties.
+ * <p>See {@link #hasSetting(String)} with {@code PROPERTY_} constants for testing for the presence
+ * of individual settings.
  *
  * @hide
  */
@@ -59,80 +59,82 @@
             };
 
     /** All configuration properties */
-    @StringDef(PROPERTY_AUTO_DETECTION_ENABLED)
+    @StringDef({ SETTING_AUTO_DETECTION_ENABLED, SETTING_GEO_DETECTION_ENABLED })
     @Retention(RetentionPolicy.SOURCE)
-    @interface Property {}
+    @interface Setting {}
 
     /** See {@link TimeZoneConfiguration#isAutoDetectionEnabled()} for details. */
-    @Property
-    public static final String PROPERTY_AUTO_DETECTION_ENABLED = "autoDetectionEnabled";
+    @Setting
+    public static final String SETTING_AUTO_DETECTION_ENABLED = "autoDetectionEnabled";
 
     /** See {@link TimeZoneConfiguration#isGeoDetectionEnabled()} for details. */
-    @Property
-    public static final String PROPERTY_GEO_DETECTION_ENABLED = "geoDetectionEnabled";
+    @Setting
+    public static final String SETTING_GEO_DETECTION_ENABLED = "geoDetectionEnabled";
 
-    private final Bundle mBundle;
+    private final @UserIdInt int mUserId;
+    @NonNull private final Bundle mBundle;
 
     private TimeZoneConfiguration(Builder builder) {
-        this.mBundle = builder.mBundle;
+        this.mUserId = builder.mUserId;
+        this.mBundle = Objects.requireNonNull(builder.mBundle);
     }
 
     private static TimeZoneConfiguration createFromParcel(Parcel in) {
-        return new TimeZoneConfiguration.Builder()
+        return new TimeZoneConfiguration.Builder(in.readInt())
                 .setPropertyBundleInternal(in.readBundle())
                 .build();
     }
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mUserId);
         dest.writeBundle(mBundle);
     }
 
-    /** Returns {@code true} if all known properties are set. */
-    public boolean isComplete() {
-        return hasProperty(PROPERTY_AUTO_DETECTION_ENABLED)
-                && hasProperty(PROPERTY_GEO_DETECTION_ENABLED);
+    /** Returns the ID of the user this configuration is associated with. */
+    public @UserIdInt int getUserId() {
+        return mUserId;
     }
 
-    /** Returns true if the specified property is set. */
-    public boolean hasProperty(@Property String property) {
-        return mBundle.containsKey(property);
+    /** Returns {@code true} if all known settings are present. */
+    public boolean isComplete() {
+        return hasSetting(SETTING_AUTO_DETECTION_ENABLED)
+                && hasSetting(SETTING_GEO_DETECTION_ENABLED);
+    }
+
+    /** Returns true if the specified setting is set. */
+    public boolean hasSetting(@Setting String setting) {
+        return mBundle.containsKey(setting);
     }
 
     /**
-     * Returns the value of the {@link #PROPERTY_AUTO_DETECTION_ENABLED} property. This
+     * Returns the value of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting. This
      * controls whether a device will attempt to determine the time zone automatically using
-     * contextual information.
+     * contextual information if the device supports auto detection.
      *
-     * @throws IllegalStateException if the field has not been set
+     * <p>This setting is global and can be updated by some users.
+     *
+     * @throws IllegalStateException if the setting has not been set
      */
     public boolean isAutoDetectionEnabled() {
-        if (!mBundle.containsKey(PROPERTY_AUTO_DETECTION_ENABLED)) {
-            throw new IllegalStateException(PROPERTY_AUTO_DETECTION_ENABLED + " is not set");
-        }
-        return mBundle.getBoolean(PROPERTY_AUTO_DETECTION_ENABLED);
+        enforceSettingPresent(SETTING_AUTO_DETECTION_ENABLED);
+        return mBundle.getBoolean(SETTING_AUTO_DETECTION_ENABLED);
     }
 
     /**
-     * Returns the value of the {@link #PROPERTY_GEO_DETECTION_ENABLED} property. This
-     * controls whether a device can use location to determine time zone. Only used when
-     * {@link #isAutoDetectionEnabled()} is true.
+     * Returns the value of the {@link #SETTING_GEO_DETECTION_ENABLED} setting. This
+     * controls whether a device can use geolocation to determine time zone. Only used when
+     * {@link #isAutoDetectionEnabled()} is {@code true} and when the user has allowed their
+     * location to be used.
      *
-     * @throws IllegalStateException if the field has not been set
+     * <p>This setting is user-scoped and can be updated by some users.
+     * See {@link TimeZoneCapabilities#getConfigureGeoDetectionEnabled()}.
+     *
+     * @throws IllegalStateException if the setting has not been set
      */
     public boolean isGeoDetectionEnabled() {
-        if (!mBundle.containsKey(PROPERTY_GEO_DETECTION_ENABLED)) {
-            throw new IllegalStateException(PROPERTY_GEO_DETECTION_ENABLED + " is not set");
-        }
-        return mBundle.getBoolean(PROPERTY_GEO_DETECTION_ENABLED);
-    }
-
-    /**
-     * Convenience method to merge this with another. The argument configuration properties have
-     * precedence.
-     */
-    public TimeZoneConfiguration with(TimeZoneConfiguration other) {
-        return new Builder(this).mergeProperties(other).build();
+        enforceSettingPresent(SETTING_GEO_DETECTION_ENABLED);
+        return mBundle.getBoolean(SETTING_GEO_DETECTION_ENABLED);
     }
 
     @Override
@@ -149,43 +151,61 @@
             return false;
         }
         TimeZoneConfiguration that = (TimeZoneConfiguration) o;
-        return mBundle.kindofEquals(that.mBundle);
+        return mUserId == that.mUserId
+                && mBundle.kindofEquals(that.mBundle);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mBundle);
+        return Objects.hash(mUserId, mBundle);
     }
 
     @Override
     public String toString() {
         return "TimeZoneDetectorConfiguration{"
+                + "mUserId=" + mUserId
                 + "mBundle=" + mBundle
                 + '}';
     }
 
+    private void enforceSettingPresent(@Setting String setting) {
+        if (!mBundle.containsKey(setting)) {
+            throw new IllegalStateException(setting + " is not set");
+        }
+    }
+
     /** @hide */
     public static class Builder {
 
-        private Bundle mBundle = new Bundle();
+        private final @UserIdInt int mUserId;
+        private final Bundle mBundle = new Bundle();
 
         /**
-         * Creates a new Builder with no properties set.
+         * Creates a new Builder for a userId with no settings held.
          */
-        public Builder() {}
+        public Builder(@UserIdInt int userId) {
+            mUserId = userId;
+        }
 
         /**
-         * Creates a new Builder by copying properties from an existing instance.
+         * Creates a new Builder by copying the user ID and settings from an existing instance.
          */
         public Builder(TimeZoneConfiguration toCopy) {
+            this.mUserId = toCopy.mUserId;
             mergeProperties(toCopy);
         }
 
         /**
-         * Merges {@code other} properties into this instances, replacing existing values in this
-         * where the properties appear in both.
+         * Merges {@code other} settings into this instances, replacing existing values in this
+         * where the settings appear in both.
          */
         public Builder mergeProperties(TimeZoneConfiguration other) {
+            if (mUserId != other.mUserId) {
+                throw new IllegalArgumentException(
+                        "Cannot merge configurations for different user IDs."
+                                + " this.mUserId=" + this.mUserId
+                                + ", other.mUserId=" + other.mUserId);
+            }
             this.mBundle.putAll(other.mBundle);
             return this;
         }
@@ -195,15 +215,19 @@
             return this;
         }
 
-        /** Sets the desired state of the automatic time zone detection property. */
+        /**
+         * Sets the state of the {@link #SETTING_AUTO_DETECTION_ENABLED} setting.
+         */
         public Builder setAutoDetectionEnabled(boolean enabled) {
-            this.mBundle.putBoolean(PROPERTY_AUTO_DETECTION_ENABLED, enabled);
+            this.mBundle.putBoolean(SETTING_AUTO_DETECTION_ENABLED, enabled);
             return this;
         }
 
-        /** Sets the desired state of the geolocation time zone detection enabled property. */
+        /**
+         * Sets the state of the {@link #SETTING_GEO_DETECTION_ENABLED} setting.
+         */
         public Builder setGeoDetectionEnabled(boolean enabled) {
-            this.mBundle.putBoolean(PROPERTY_GEO_DETECTION_ENABLED, enabled);
+            this.mBundle.putBoolean(SETTING_GEO_DETECTION_ENABLED, enabled);
             return this;
         }
 
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index 7885613..2b1cbf2 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -37,37 +37,29 @@
     TimeZoneCapabilities getCapabilities();
 
     /**
-     * Returns the current user's complete time zone configuration. See {@link
-     * TimeZoneConfiguration}.
-     */
-    @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
-    @NonNull
-    TimeZoneConfiguration getConfiguration();
-
-    /**
      * Modifies the time zone detection configuration.
      *
-     * <p>Configuration properties vary in scope: some may be device-wide, others may be specific to
-     * the current user.
+     * <p>Configuration settings vary in scope: some may be global (affect all users), others may be
+     * specific to the current user.
      *
-     * <p>The ability to modify configuration properties can be subject to restrictions. For
+     * <p>The ability to modify configuration settings can be subject to restrictions. For
      * example, they may be determined by device hardware, general policy (i.e. only the primary
-     * user can set them), or by a managed device policy. See {@link #getCapabilities()} to obtain
+     * user can set them), or by a managed device policy. Use {@link #getCapabilities()} to obtain
      * information at runtime about the user's capabilities.
      *
-     * <p>Attempts to set configuration with capabilities that are {@link
+     * <p>Attempts to modify configuration settings with capabilities that are {@link
      * TimeZoneCapabilities#CAPABILITY_NOT_SUPPORTED} or {@link
      * TimeZoneCapabilities#CAPABILITY_NOT_ALLOWED} will have no effect and a {@code false}
-     * will be returned. Setting configuration with capabilities that are {@link
+     * will be returned. Modifying configuration settings with capabilities that are {@link
      * TimeZoneCapabilities#CAPABILITY_NOT_APPLICABLE} or {@link
      * TimeZoneCapabilities#CAPABILITY_POSSESSED} will succeed. See {@link
      * TimeZoneCapabilities} for further details.
      *
-     * <p>If the configuration is not "complete", then only the specified properties will be
-     * updated (where the user's capabilities allow) and other settings will be left unchanged. See
-     * {@link TimeZoneConfiguration#isComplete()}.
+     * <p>If the supplied configuration only has some values set, then only the specified settings
+     * will be updated (where the user's capabilities allow) and other settings will be left
+     * unchanged.
      *
-     * @return {@code true} if all the configuration properties specified have been set to the
+     * @return {@code true} if all the configuration settings specified have been set to the
      *   new values, {@code false} if none have
      */
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
@@ -76,14 +68,20 @@
     /**
      * An interface that can be used to listen for changes to the time zone detector configuration.
      */
+    @FunctionalInterface
     interface TimeZoneConfigurationListener {
-        /** Called when the configuration changes. There are no guarantees about the thread used. */
-        void onChange(@NonNull TimeZoneConfiguration configuration);
+        /**
+         * Called when something about the time zone configuration on the device has changed.
+         * This could be because the current user has changed, one of the device's relevant settings
+         * has changed, or something that could affect a user's capabilities has changed.
+         * There are no guarantees about the thread used.
+         */
+        void onChange();
     }
 
     /**
-     * Registers a listener that will be informed when the configuration changes. The complete
-     * configuration is passed to the listener, not just the properties that have changed.
+     * Registers a listener that will be informed when something about the time zone configuration
+     * changes.
      */
     @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     void addConfigurationListener(@NonNull TimeZoneConfigurationListener listener);
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
index 0770aff..4c69732 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetectorImpl.java
@@ -57,19 +57,6 @@
     }
 
     @Override
-    @NonNull
-    public TimeZoneConfiguration getConfiguration() {
-        if (DEBUG) {
-            Log.d(TAG, "getConfiguration called");
-        }
-        try {
-            return mITimeZoneDetectorService.getConfiguration();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    @Override
     public boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration) {
         if (DEBUG) {
             Log.d(TAG, "updateConfiguration called: " + configuration);
@@ -94,8 +81,8 @@
                 ITimeZoneConfigurationListener iListener =
                         new ITimeZoneConfigurationListener.Stub() {
                     @Override
-                    public void onChange(@NonNull TimeZoneConfiguration configuration) {
-                        notifyConfigurationListeners(configuration);
+                    public void onChange() {
+                        notifyConfigurationListeners();
                     }
                 };
                 mConfigurationReceiver = iListener;
@@ -116,14 +103,14 @@
         }
     }
 
-    private void notifyConfigurationListeners(@NonNull TimeZoneConfiguration configuration) {
+    private void notifyConfigurationListeners() {
         final ArraySet<TimeZoneConfigurationListener> configurationListeners;
         synchronized (this) {
             configurationListeners = new ArraySet<>(mConfigurationListeners);
         }
         int size = configurationListeners.size();
         for (int i = 0; i < size; i++) {
-            configurationListeners.valueAt(i).onChange(configuration);
+            configurationListeners.valueAt(i).onChange();
         }
     }
 
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index d2a774b..c7b20b2 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2674,52 +2674,6 @@
     }
 
     /**
-     * Construct an encrypted, RFCOMM server socket.
-     * Call #accept to retrieve connections to this socket.
-     *
-     * @return An RFCOMM BluetoothServerSocket
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * permissions.
-     * @hide
-     */
-    public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port) throws IOException {
-        BluetoothServerSocket socket =
-                new BluetoothServerSocket(BluetoothSocket.TYPE_RFCOMM, false, true, port);
-        int errno = socket.mSocket.bindListen();
-        if (port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
-            socket.setChannel(socket.mSocket.getPort());
-        }
-        if (errno < 0) {
-            //TODO(BT): Throw the same exception error code
-            // that the previous code was using.
-            //socket.mSocket.throwErrnoNative(errno);
-            throw new IOException("Error: " + errno);
-        }
-        return socket;
-    }
-
-    /**
-     * Construct a SCO server socket.
-     * Call #accept to retrieve connections to this socket.
-     *
-     * @return A SCO BluetoothServerSocket
-     * @throws IOException On error, for example Bluetooth not available, or insufficient
-     * permissions.
-     * @hide
-     */
-    public static BluetoothServerSocket listenUsingScoOn() throws IOException {
-        BluetoothServerSocket socket =
-                new BluetoothServerSocket(BluetoothSocket.TYPE_SCO, false, false, -1);
-        int errno = socket.mSocket.bindListen();
-        if (errno < 0) {
-            //TODO(BT): Throw the same exception error code
-            // that the previous code was using.
-            //socket.mSocket.throwErrnoNative(errno);
-        }
-        return socket;
-    }
-
-    /**
      * Construct an encrypted, authenticated, L2CAP server socket.
      * Call #accept to retrieve connections to this socket.
      * <p>To auto assign a port without creating a SDP record use
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt b/core/java/android/content/pm/FileChecksum.aidl
similarity index 76%
copy from packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
copy to core/java/android/content/pm/FileChecksum.aidl
index 9d05843..109f211 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
+++ b/core/java/android/content/pm/FileChecksum.aidl
@@ -14,11 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.settings
+package android.content.pm;
 
-import android.content.ContentResolver
+parcelable FileChecksum;
 
-interface CurrentUserContentResolverProvider {
-
-    val currentUserContentResolver: ContentResolver
-}
\ No newline at end of file
diff --git a/core/java/android/content/pm/FileChecksum.java b/core/java/android/content/pm/FileChecksum.java
new file mode 100644
index 0000000..55430c2
--- /dev/null
+++ b/core/java/android/content/pm/FileChecksum.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 android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.IntentSender;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+/**
+ * A typed checksum.
+ *
+ * @see PackageManager#getChecksums(String, boolean, int, List, IntentSender)
+ */
+@DataClass(genHiddenConstructor = true)
+public final class FileChecksum implements Parcelable {
+    /**
+     * Checksum for which split. Null indicates base.apk.
+     */
+    private final @Nullable String mSplitName;
+    /**
+     * Checksum kind.
+     */
+    private final @PackageManager.FileChecksumKind int mKind;
+    /**
+     * Checksum value.
+     */
+    private final @NonNull byte[] mValue;
+    /**
+     * For Installer-provided checksums, certificate of the Installer/AppStore.
+     */
+    private final @Nullable byte[] mSourceCertificate;
+
+    /**
+     * Constructor, internal use only
+     *
+     * @hide
+     */
+    public FileChecksum(@Nullable String splitName, @PackageManager.FileChecksumKind int kind,
+            @NonNull byte[] value) {
+        this(splitName, kind, value, (byte[]) null);
+    }
+
+    /**
+     * Constructor, internal use only
+     *
+     * @hide
+     */
+    public FileChecksum(@Nullable String splitName, @PackageManager.FileChecksumKind int kind,
+            @NonNull byte[] value, @Nullable Certificate sourceCertificate)
+            throws CertificateEncodingException {
+        this(splitName, kind, value,
+                (sourceCertificate != null) ? sourceCertificate.getEncoded() : null);
+    }
+
+    /**
+     * Certificate of the source of this checksum.
+     * @throws CertificateException in case when certificate can't be re-created from serialized
+     * data.
+     */
+    public @Nullable Certificate getSourceCertificate() throws CertificateException {
+        if (mSourceCertificate == null) {
+            return null;
+        }
+        final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        final InputStream is = new ByteArrayInputStream(mSourceCertificate);
+        final X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
+        return cert;
+    }
+
+
+
+    // Code below generated by codegen v1.0.15.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/FileChecksum.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new FileChecksum.
+     *
+     * @param splitName
+     *   Checksum for which split. Null indicates base.apk.
+     * @param kind
+     *   Checksum kind.
+     * @param value
+     *   Checksum value.
+     * @param sourceCertificate
+     *   For Installer-provided checksums, certificate of the Installer/AppStore.
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public FileChecksum(
+            @Nullable String splitName,
+            @PackageManager.FileChecksumKind int kind,
+            @NonNull byte[] value,
+            @Nullable byte[] sourceCertificate) {
+        this.mSplitName = splitName;
+        this.mKind = kind;
+        com.android.internal.util.AnnotationValidations.validate(
+                PackageManager.FileChecksumKind.class, null, mKind);
+        this.mValue = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mValue);
+        this.mSourceCertificate = sourceCertificate;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Checksum for which split. Null indicates base.apk.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getSplitName() {
+        return mSplitName;
+    }
+
+    /**
+     * Checksum kind.
+     */
+    @DataClass.Generated.Member
+    public @PackageManager.FileChecksumKind int getKind() {
+        return mKind;
+    }
+
+    /**
+     * Checksum value.
+     */
+    @DataClass.Generated.Member
+    public @NonNull byte[] getValue() {
+        return mValue;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mSplitName != null) flg |= 0x1;
+        if (mSourceCertificate != null) flg |= 0x8;
+        dest.writeByte(flg);
+        if (mSplitName != null) dest.writeString(mSplitName);
+        dest.writeInt(mKind);
+        dest.writeByteArray(mValue);
+        if (mSourceCertificate != null) dest.writeByteArray(mSourceCertificate);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ FileChecksum(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        String splitName = (flg & 0x1) == 0 ? null : in.readString();
+        int kind = in.readInt();
+        byte[] value = in.createByteArray();
+        byte[] sourceCertificate = (flg & 0x8) == 0 ? null : in.createByteArray();
+
+        this.mSplitName = splitName;
+        this.mKind = kind;
+        com.android.internal.util.AnnotationValidations.validate(
+                PackageManager.FileChecksumKind.class, null, mKind);
+        this.mValue = value;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mValue);
+        this.mSourceCertificate = sourceCertificate;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<FileChecksum> CREATOR
+            = new Parcelable.Creator<FileChecksum>() {
+        @Override
+        public FileChecksum[] newArray(int size) {
+            return new FileChecksum[size];
+        }
+
+        @Override
+        public FileChecksum createFromParcel(@NonNull Parcel in) {
+            return new FileChecksum(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1598322801861L,
+            codegenVersion = "1.0.15",
+            sourceFile = "frameworks/base/core/java/android/content/pm/FileChecksum.java",
+            inputSignatures = "private final @android.annotation.Nullable java.lang.String mSplitName\nprivate final @android.content.pm.PackageManager.FileChecksumKind int mKind\nprivate final @android.annotation.NonNull byte[] mValue\nprivate final @android.annotation.Nullable byte[] mSourceCertificate\npublic @android.annotation.Nullable java.security.cert.Certificate getSourceCertificate()\nclass FileChecksum extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 6a8dd81..1f8cee2 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -743,6 +743,8 @@
 
     void notifyPackagesReplacedReceived(in String[] packages);
 
+    void getChecksums(in String packageName, boolean includeSplits, int optional, int required, in List trustedInstallers, in IntentSender statusReceiver, int userId);
+
     //------------------------------------------------------------------------
     //
     // The following binder interfaces have been moved to IPermissionManager
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index bed7b26..b7c3289 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2076,7 +2076,7 @@
                 STAGED_SESSION_VERIFICATION_FAILED,
                 STAGED_SESSION_ACTIVATION_FAILED,
                 STAGED_SESSION_UNKNOWN,
-                STAGED_SESSION_OTHER_ERROR})
+                STAGED_SESSION_CONFLICT})
         @Retention(RetentionPolicy.SOURCE)
         public @interface StagedSessionErrorCode{}
         /**
@@ -2103,10 +2103,10 @@
         public static final int STAGED_SESSION_UNKNOWN = 3;
 
         /**
-         * Constant indicating that a known error occurred while processing this staged session, but
-         * the error could not be matched to other categories.
+         * Constant indicating that the session was in conflict with another staged session and had
+         * to be sacrificed for resolution.
          */
-        public static final int STAGED_SESSION_OTHER_ERROR = 4;
+        public static final int STAGED_SESSION_CONFLICT = 4;
 
         /** {@hide} */
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7b2955d..da8d15a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -79,8 +79,11 @@
 import dalvik.system.VMRuntime;
 
 import java.io.File;
+import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
 import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
@@ -3305,6 +3308,13 @@
     public static final String EXTRA_FAILURE_EXISTING_PERMISSION
             = "android.content.pm.extra.FAILURE_EXISTING_PERMISSION";
 
+    /**
+     * Extra field name for the ID of a package pending verification. Passed to
+     * a package verifier and is used to call back to
+     * @see #getChecksums
+     */
+    public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
+
    /**
     * Permission flag: The permission is set in its current state
     * by the user and apps can still request it at runtime.
@@ -7842,6 +7852,114 @@
     }
 
     /**
+     * Root SHA256 hash of a 4K Merkle tree computed over all file bytes.
+     * <a href="https://source.android.com/security/apksigning/v4">See APK Signature Scheme V4</a>.
+     * <a href="https://git.kernel.org/pub/scm/fs/fscrypt/fscrypt.git/tree/Documentation/filesystems/fsverity.rst">See fs-verity</a>.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 0x00000001;
+
+    /**
+     * MD5 hash computed over all file bytes.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_MD5 = 0x00000002;
+
+    /**
+     * SHA1 hash computed over all file bytes.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_SHA1 = 0x00000004;
+
+    /**
+     * SHA256 hash computed over all file bytes.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_SHA256 = 0x00000008;
+
+    /**
+     * SHA512 hash computed over all file bytes.
+     *
+     * @see #getChecksums
+     */
+    public static final int WHOLE_SHA512 = 0x00000010;
+
+    /**
+     * Root SHA256 hash of a 1M Merkle tree computed over protected content.
+     * Excludes signing block.
+     * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
+     *
+     * @see #getChecksums
+     */
+    public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 0x00000020;
+
+    /**
+     * Root SHA512 hash of a 1M Merkle tree computed over protected content.
+     * Excludes signing block.
+     * <a href="https://source.android.com/security/apksigning/v2">See APK Signature Scheme V2</a>.
+     *
+     * @see #getChecksums
+     */
+    public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 0x00000040;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"WHOLE_", "PARTIAL_"}, value = {
+            WHOLE_MERKLE_ROOT_4K_SHA256,
+            WHOLE_MD5,
+            WHOLE_SHA1,
+            WHOLE_SHA256,
+            WHOLE_SHA512,
+            PARTIAL_MERKLE_ROOT_1M_SHA256,
+            PARTIAL_MERKLE_ROOT_1M_SHA512,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FileChecksumKind {}
+
+    /**
+     * Trust any Installer to provide checksums for the package.
+     * @see #getChecksums
+     */
+    public static final @Nullable List<Certificate> TRUST_ALL = null;
+
+    /**
+     * Don't trust any Installer to provide checksums for the package.
+     * This effectively disables optimized Installer-enforced checksums.
+     * @see #getChecksums
+     */
+    public static final @NonNull List<Certificate> TRUST_NONE = Collections.emptyList();
+
+    /**
+     * Returns the checksums for APKs within a package.
+     *
+     * By default returns all readily available checksums:
+     * - enforced by platform,
+     * - enforced by installer.
+     * If caller needs a specific checksum kind, they can specify it as required.
+     *
+     * @param packageName whose checksums to return.
+     * @param includeSplits whether to include checksums for non-base splits.
+     * @param required explicitly request the checksum kinds. Will incur significant
+     *                 CPU/memory/disk usage.
+     * @param trustedInstallers for checksums enforced by Installer, which ones to be trusted.
+     *                          {@link #TRUST_ALL} will return checksums from any Installer,
+     *                          {@link #TRUST_NONE} disables optimized Installer-enforced checksums.
+     * @param statusReceiver called once when the results are available as
+     *                       {@link #EXTRA_CHECKSUMS} of type FileChecksum[].
+     * @throws CertificateEncodingException if an encoding error occurs for trustedInstallers.
+     * @throws NameNotFoundException if a package with the given name cannot be found on the system.
+     */
+    public void getChecksums(@NonNull String packageName, boolean includeSplits,
+            @FileChecksumKind int required, @Nullable List<Certificate> trustedInstallers,
+            @NonNull IntentSender statusReceiver)
+            throws CertificateEncodingException, IOException, NameNotFoundException {
+        throw new UnsupportedOperationException("getChecksums not implemented in subclass");
+    }
+
+    /**
      * @return the default text classifier package name, or null if there's none.
      *
      * @hide
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2006048..0f1f276 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -88,7 +88,6 @@
 import android.util.SparseArray;
 import android.util.TypedValue;
 import android.util.apk.ApkSignatureVerifier;
-import android.view.Display;
 import android.view.Gravity;
 
 import com.android.internal.R;
@@ -8536,7 +8535,7 @@
                 null,
                 androidAppInfo.resourceDirs,
                 androidAppInfo.sharedLibraryFiles,
-                Display.DEFAULT_DISPLAY,
+                null,
                 null,
                 systemResources.getCompatibilityInfo(),
                 systemResources.getClassLoader(),
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
index 9da0f20..fcb80aa 100644
--- a/core/java/android/content/res/ResourcesKey.java
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -16,6 +16,8 @@
 
 package android.content.res;
 
+import static android.os.Build.VERSION_CODES.O;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -41,8 +43,17 @@
     @Nullable
     public final String[] mLibDirs;
 
-    public final int mDisplayId;
+    /**
+     * The display ID that overrides the global resources display to produce the Resources display.
+     * If set to something other than {@link android.view.Display#INVALID_DISPLAY} this will
+     * override the global resources display for this key.
+     */
+    @UnsupportedAppUsage(maxTargetSdk = O)
+    public int mDisplayId;
 
+    /**
+     * The configuration applied to the global configuration to produce the Resources configuration.
+     */
     @NonNull
     public final Configuration mOverrideConfiguration;
 
@@ -58,7 +69,7 @@
                         @Nullable String[] splitResDirs,
                         @Nullable String[] overlayDirs,
                         @Nullable String[] libDirs,
-                        int displayId,
+                        int overrideDisplayId,
                         @Nullable Configuration overrideConfig,
                         @Nullable CompatibilityInfo compatInfo,
                         @Nullable ResourcesLoader[] loader) {
@@ -67,7 +78,7 @@
         mOverlayDirs = overlayDirs;
         mLibDirs = libDirs;
         mLoaders = (loader != null && loader.length == 0) ? null : loader;
-        mDisplayId = displayId;
+        mDisplayId = overrideDisplayId;
         mOverrideConfiguration = new Configuration(overrideConfig != null
                 ? overrideConfig : Configuration.EMPTY);
         mCompatInfo = compatInfo != null ? compatInfo : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
@@ -77,7 +88,7 @@
         hash = 31 * hash + Arrays.hashCode(mSplitResDirs);
         hash = 31 * hash + Arrays.hashCode(mOverlayDirs);
         hash = 31 * hash + Arrays.hashCode(mLibDirs);
-        hash = 31 * hash + mDisplayId;
+        hash = 31 * hash + Objects.hashCode(mDisplayId);
         hash = 31 * hash + Objects.hashCode(mOverrideConfiguration);
         hash = 31 * hash + Objects.hashCode(mCompatInfo);
         hash = 31 * hash + Arrays.hashCode(mLoaders);
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b0d4497..fecfd3c 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2954,7 +2954,7 @@
      * enough current data to make a decision, or the battery is currently
      * charging.
      *
-     * @param curTime The current elepsed realtime in microseconds.
+     * @param curTime The current elapsed realtime in microseconds.
      */
     @UnsupportedAppUsage
     public abstract long computeBatteryTimeRemaining(long curTime);
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index ce6c0ff..a92d91b 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -18,6 +18,7 @@
 package android.os;
 
 import android.os.BatterySaverPolicyConfig;
+import android.os.ParcelDuration;
 import android.os.PowerSaveState;
 import android.os.WorkSource;
 
@@ -58,6 +59,9 @@
     boolean setAdaptivePowerSavePolicy(in BatterySaverPolicyConfig config);
     boolean setAdaptivePowerSaveEnabled(boolean enabled);
     int getPowerSaveModeTrigger();
+    void setBatteryDischargePrediction(in ParcelDuration timeRemaining, boolean isCustomized);
+    ParcelDuration getBatteryDischargePrediction();
+    boolean isBatteryDischargePredictionPersonalized();
     boolean isDeviceIdleMode();
     boolean isLightDeviceIdleMode();
 
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt b/core/java/android/os/ParcelDuration.aidl
similarity index 76%
copy from packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
copy to core/java/android/os/ParcelDuration.aidl
index 9d05843..e1aa109 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
+++ b/core/java/android/os/ParcelDuration.aidl
@@ -14,11 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.systemui.settings
+package android.os;
 
-import android.content.ContentResolver
-
-interface CurrentUserContentResolverProvider {
-
-    val currentUserContentResolver: ContentResolver
-}
\ No newline at end of file
+parcelable ParcelDuration cpp_header "android/ParcelDuration.h";
\ No newline at end of file
diff --git a/core/java/android/os/ParcelDuration.java b/core/java/android/os/ParcelDuration.java
new file mode 100644
index 0000000..37cde31
--- /dev/null
+++ b/core/java/android/os/ParcelDuration.java
@@ -0,0 +1,103 @@
+/*
+ * 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;
+
+import android.annotation.NonNull;
+
+import java.time.Duration;
+
+/**
+ * Parcelable version of {@link Duration} that can be used in binder calls.
+ *
+ * @hide
+ */
+public final class ParcelDuration implements Parcelable {
+
+    private final long mSeconds;
+    private final int mNanos;
+
+    /**
+     * Construct a Duration object using the given millisecond value.
+     *
+     * @hide
+     */
+    public ParcelDuration(long ms) {
+        this(Duration.ofMillis(ms));
+    }
+
+    /**
+     * Wrap a {@link Duration} instance.
+     *
+     * @param duration The {@link Duration} instance to wrap.
+     */
+    public ParcelDuration(@NonNull Duration duration) {
+        mSeconds = duration.getSeconds();
+        mNanos = duration.getNano();
+    }
+
+    private ParcelDuration(@NonNull Parcel parcel) {
+        mSeconds = parcel.readLong();
+        mNanos = parcel.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int parcelableFlags) {
+        parcel.writeLong(mSeconds);
+        parcel.writeInt(mNanos);
+    }
+
+    /**
+     * Returns a {@link Duration} instance that's equivalent to this Duration's length.
+     *
+     * @return a {@link Duration} instance of identical length.
+     */
+    @NonNull
+    public Duration getDuration() {
+        return Duration.ofSeconds(mSeconds, mNanos);
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return getDuration().toString();
+    }
+
+    /**
+     * Creator for Duration.
+     */
+    @NonNull
+    public static final Parcelable.Creator<ParcelDuration> CREATOR =
+            new Parcelable.Creator<ParcelDuration>() {
+
+        @Override
+        @NonNull
+        public ParcelDuration createFromParcel(@NonNull Parcel source) {
+            return new ParcelDuration(source);
+        }
+
+        @Override
+        @NonNull
+        public ParcelDuration[] newArray(int size) {
+            return new ParcelDuration[size];
+        }
+    };
+}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 309805f..ed38b3f 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -41,6 +41,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.time.Duration;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicLong;
 
@@ -1719,6 +1720,70 @@
     }
 
     /**
+     * Allows an app to tell the system how long it believes the battery will last and whether
+     * this estimate is customized based on historical device usage or on a generic configuration.
+     * These estimates will be displayed on system UI surfaces in place of the system computed
+     * value.
+     *
+     * Calling this requires the {@link android.Manifest.permission#DEVICE_POWER} permission.
+     *
+     * @param timeRemaining  The time remaining as a {@link Duration}.
+     * @param isPersonalized true if personalized based on device usage history, false otherwise.
+     * @throws IllegalStateException if the device is powered or currently charging
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public void setBatteryDischargePrediction(@NonNull Duration timeRemaining,
+            boolean isPersonalized) {
+        if (timeRemaining == null) {
+            throw new IllegalArgumentException("time remaining must not be null");
+        }
+        try {
+            mService.setBatteryDischargePrediction(new ParcelDuration(timeRemaining),
+                    isPersonalized);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current battery life remaining estimate.
+     *
+     * @return The estimated battery life remaining as a {@link Duration}. Will be {@code null} if
+     * the device is powered, charging, or an error was encountered.
+     */
+    @Nullable
+    public Duration getBatteryDischargePrediction() {
+        try {
+            final ParcelDuration parcelDuration = mService.getBatteryDischargePrediction();
+            if (parcelDuration == null) {
+                return null;
+            }
+            return parcelDuration.getDuration();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns whether the current battery life remaining estimate is personalized based on device
+     * usage history or not. This value does not take a device's powered or charging state into
+     * account.
+     *
+     * @return A boolean indicating if the current discharge estimate is personalized based on
+     * historical device usage or not.
+     */
+    public boolean isBatteryDischargePredictionPersonalized() {
+        try {
+            return mService.isBatteryDischargePredictionPersonalized();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Get data about the battery saver mode for a specific service
      * @param serviceType unique key for the service, one of {@link ServiceType}
      * @return Battery saver state data.
@@ -2185,6 +2250,18 @@
     }
 
     /**
+     * Intent that is broadcast when the enhanced battery discharge prediction changes. The new
+     * value can be retrieved via {@link #getBatteryDischargePrediction()}.
+     * This broadcast is only sent to registered receivers.
+     *
+     * @hide
+     */
+    @TestApi
+    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED =
+            "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED";
+
+    /**
      * Intent that is broadcast when the state of {@link #isPowerSaveMode()} changes.
      * This broadcast is only sent to registered receivers.
      */
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 2465b0e..81a147c 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -43,7 +43,6 @@
 import android.content.IntentSender;
 import android.content.pm.UserInfo;
 import android.content.pm.UserInfo.UserInfoFlag;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
@@ -3174,28 +3173,55 @@
     }
 
     /**
-     * Returns information for all users on this device, including ones marked for deletion.
-     * To retrieve only users that are alive, use {@link #getUsers(boolean)}.
+     * Returns information for all fully-created users on this device, including ones marked for
+     * deletion.
      *
-     * @return the list of users that exist on the device.
+     * <p>To retrieve only users that are not marked for deletion, use {@link #getAliveUsers()}.
+     *
+     * <p>To retrieve *all* users (including partial and pre-created users), use
+     * {@link #getUsers(boolean, boolean, boolean)) getUsers(false, false, false)}.
+     *
+     * <p>To retrieve a more specific list of users, use
+     * {@link #getUsers(boolean, boolean, boolean)}.
+     *
+     * @return the list of users that were created.
+     *
      * @hide
      */
     @UnsupportedAppUsage
     @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public List<UserInfo> getUsers() {
-        return getUsers(/* excludeDying= */ false);
+        return getUsers(/*excludePartial= */ true, /* excludeDying= */ false,
+                /* excludePreCreated= */ true);
     }
 
     /**
-     * Returns information for all users on this device. Requires
-     * {@link android.Manifest.permission#MANAGE_USERS} permission.
+     * Returns information for all "usable" users on this device (i.e, it excludes users that are
+     * marked for deletion, pre-created users, etc...).
      *
-     * @param excludeDying specify if the list should exclude users being
-     *            removed.
+     * <p>To retrieve all fully-created users, use {@link #getUsers()}.
+     *
+     * <p>To retrieve a more specific list of users, use
+     * {@link #getUsers(boolean, boolean, boolean)}.
+     *
      * @return the list of users that were created.
      * @hide
      */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public @NonNull List<UserInfo> getAliveUsers() {
+        return getUsers(/*excludePartial= */ true, /* excludeDying= */ true,
+                /* excludePreCreated= */ true);
+    }
+
+    /**
+     * @deprecated use {@link #getAliveUsers()} for {@code getUsers(true)}, or
+     * {@link #getUsers()} for @code getUsers(false)}.
+     *
+     * @hide
+     */
+    @Deprecated
     @UnsupportedAppUsage
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
         return getUsers(/*excludePartial= */ true, excludeDying,
                 /* excludePreCreated= */ true);
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index 61e6a05..be8b929 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -90,9 +90,10 @@
     int unlink(int storageId, in @utf8InCpp String path);
 
     /**
-     * Checks if a file's certain range is loaded. File is specified by its path.
+     * Returns overall loading progress of all the files on a storage, progress value between [0,1].
+     * Returns a negative value on error.
      */
-    boolean isFileRangeLoaded(int storageId, in @utf8InCpp String path, long start, long end);
+    float getLoadingProgress(int storageId);
 
     /**
      * Reads the metadata of a file. File is specified by either its path or 16 byte id.
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index ca6114f..b8dbfbb 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -304,29 +304,21 @@
     }
 
     /**
-     * Checks whether a file under the current storage directory is fully loaded.
+     * Returns the loading progress of a storage
      *
-     * @param path The relative path of the file.
-     * @return True if the file is fully loaded.
+     * @return progress value between [0, 1].
      */
-    public boolean isFileFullyLoaded(@NonNull String path) {
-        return isFileRangeLoaded(path, 0, -1);
-    }
-
-    /**
-     * Checks whether a range in a file if loaded.
-     *
-     * @param path The relative path of the file.
-     * @param start            The starting offset of the range.
-     * @param end              The ending offset of the range.
-     * @return True if the file is fully loaded.
-     */
-    public boolean isFileRangeLoaded(@NonNull String path, long start, long end) {
+    public float getLoadingProgress() throws IOException {
         try {
-            return mService.isFileRangeLoaded(mId, path, start, end);
+            final float res = mService.getLoadingProgress(mId);
+            if (res < 0) {
+                throw new IOException(
+                        "getLoadingProgress() failed at querying loading progress, errno " + -res);
+            }
+            return res;
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
-            return false;
+            return 0;
         }
     }
 
diff --git a/core/java/android/preference/DialogPreference.java b/core/java/android/preference/DialogPreference.java
index dfdb57c4..fb7b232 100644
--- a/core/java/android/preference/DialogPreference.java
+++ b/core/java/android/preference/DialogPreference.java
@@ -36,8 +36,6 @@
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
 import android.widget.TextView;
 
 /**
@@ -337,9 +335,6 @@
         if (state != null) {
             dialog.onRestoreInstanceState(state);
         }
-        if (needInputMethod()) {
-            requestInputMethod(dialog);
-        }
         dialog.setOnShowListener(new DialogInterface.OnShowListener() {
             @Override
             public void onShow(DialogInterface dialog) {
@@ -380,24 +375,6 @@
     }
 
     /**
-     * Returns whether the preference needs to display a soft input method when the dialog
-     * is displayed. Default is false. Subclasses should override this method if they need
-     * the soft input method brought up automatically.
-     * @hide
-     */
-    protected boolean needInputMethod() {
-        return false;
-    }
-
-    /**
-     * Sets the required flags on the dialog window to enable input method window to show up.
-     */
-    private void requestInputMethod(Dialog dialog) {
-        Window window = dialog.getWindow();
-        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
-    }
-
-    /**
      * Creates the content view for the dialog (if a custom content view is
      * required). By default, it inflates the dialog layout resource if it is
      * set.
diff --git a/core/java/android/preference/EditTextPreference.java b/core/java/android/preference/EditTextPreference.java
index af6f184..75c5102 100644
--- a/core/java/android/preference/EditTextPreference.java
+++ b/core/java/android/preference/EditTextPreference.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.TypedArray;
+import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -28,6 +29,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
+import android.view.WindowInsets;
 import android.widget.EditText;
 
 /**
@@ -123,7 +125,7 @@
 
         EditText editText = mEditText;
         editText.setText(getText());
-        
+
         ViewParent oldParent = editText.getParent();
         if (oldParent != view) {
             if (oldParent != null) {
@@ -133,6 +135,13 @@
         }
     }
 
+    @Override
+    protected void showDialog(Bundle state) {
+        super.showDialog(state);
+        mEditText.requestFocus();
+        mEditText.getWindowInsetsController().show(WindowInsets.Type.ime());
+    }
+
     /**
      * Adds the EditText widget of this preference to the dialog's view.
      * 
@@ -183,13 +192,6 @@
         return mEditText;
     }
 
-    /** @hide */
-    @Override
-    protected boolean needInputMethod() {
-        // We want the input method to show, if possible, when dialog is displayed
-        return true;
-    }
-
     @Override
     protected Parcelable onSaveInstanceState() {
         final Parcelable superState = super.onSaveInstanceState();
diff --git a/core/java/android/preference/OWNERS b/core/java/android/preference/OWNERS
index d20511f..827134e 100644
--- a/core/java/android/preference/OWNERS
+++ b/core/java/android/preference/OWNERS
@@ -1,2 +1,3 @@
+lpf@google.com
 pavlis@google.com
 clarabayarri@google.com
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index b45a1eb..859b703 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -197,6 +197,13 @@
             "intelligence_content_suggestions";
 
     /**
+     * Namespace for JobScheduler configurations.
+     * @hide
+     */
+    @TestApi
+    public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler";
+
+    /**
      * Namespace for all media native related features.
      *
      * @hide
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 327bca2..d55fc51 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -218,8 +218,15 @@
     }
 
     /** {@hide} */
-    private void enforceTree(Uri documentUri) {
-        if (isTreeUri(documentUri)) {
+    private void enforceTreeForExtraUris(Bundle extras) {
+        enforceTree(extras.getParcelable(DocumentsContract.EXTRA_URI));
+        enforceTree(extras.getParcelable(DocumentsContract.EXTRA_PARENT_URI));
+        enforceTree(extras.getParcelable(DocumentsContract.EXTRA_TARGET_URI));
+    }
+
+    /** {@hide} */
+    private void enforceTree(@Nullable Uri documentUri) {
+        if (documentUri != null && isTreeUri(documentUri)) {
             final String parent = getTreeDocumentId(documentUri);
             final String child = getDocumentId(documentUri);
             if (Objects.equals(parent, child)) {
@@ -1076,6 +1083,9 @@
         final Context context = getContext();
         final Bundle out = new Bundle();
 
+        // If the URI is a tree URI performs some validation.
+        enforceTreeForExtraUris(extras);
+
         if (METHOD_EJECT_ROOT.equals(method)) {
             // Given that certain system apps can hold MOUNT_UNMOUNT permission, but only apps
             // signed with platform signature can hold MANAGE_DOCUMENTS, we are going to check for
@@ -1099,9 +1109,6 @@
                     "Requested authority " + authority + " doesn't match provider " + mAuthority);
         }
 
-        // If the URI is a tree URI performs some validation.
-        enforceTree(documentUri);
-
         if (METHOD_IS_CHILD_DOCUMENT.equals(method)) {
             enforceReadPermissionInner(documentUri, getCallingPackage(),
                     getCallingAttributionTag(), null);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6903a99..03cf0cf 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -65,6 +65,7 @@
 import android.os.DropBoxManager;
 import android.os.IBinder;
 import android.os.LocaleList;
+import android.os.PowerManager;
 import android.os.PowerManager.AutoPowerSaveModeTriggers;
 import android.os.Process;
 import android.os.RemoteCallback;
@@ -6403,6 +6404,17 @@
         public static final int LOCATION_MODE_ON = LOCATION_MODE_HIGH_ACCURACY;
 
         /**
+         * The current location time zone detection enabled state for the user.
+         *
+         * See {@link
+         * android.app.timezonedetector.TimeZoneDetector#getCapabilities} for access. See {@link
+         * android.app.timezonedetector.TimeZoneDetector#updateConfiguration} to update.
+         * @hide
+         */
+        public static final String LOCATION_TIME_ZONE_DETECTION_ENABLED =
+                "location_time_zone_detection_enabled";
+
+        /**
          * The accuracy in meters used for coarsening location for clients with only the coarse
          * location permission.
          *
@@ -11844,36 +11856,6 @@
         public static final String ALARM_MANAGER_CONSTANTS = "alarm_manager_constants";
 
         /**
-         * Job scheduler specific settings.
-         * This is encoded as a key=value list, separated by commas. Ex:
-         *
-         * "min_ready_jobs_count=2,moderate_use_factor=.5"
-         *
-         * The following keys are supported:
-         *
-         * <pre>
-         * min_idle_count                       (int)
-         * min_charging_count                   (int)
-         * min_connectivity_count               (int)
-         * min_content_count                    (int)
-         * min_ready_jobs_count                 (int)
-         * heavy_use_factor                     (float)
-         * moderate_use_factor                  (float)
-         * fg_job_count                         (int)
-         * bg_normal_job_count                  (int)
-         * bg_moderate_job_count                (int)
-         * bg_low_job_count                     (int)
-         * bg_critical_job_count                (int)
-         * </pre>
-         *
-         * <p>
-         * Type: string
-         * @hide
-         * @see com.android.server.job.JobSchedulerService.Constants
-         */
-        public static final String JOB_SCHEDULER_CONSTANTS = "job_scheduler_constants";
-
-        /**
          * Job scheduler QuotaController specific settings.
          * This is encoded as a key=value list, separated by commas. Ex:
          *
@@ -12545,18 +12527,23 @@
          * millis. See {@link #BATTERY_ESTIMATES_LAST_UPDATE_TIME} for the last time this value
          * was updated.
          *
+         * @deprecated Use {@link PowerManager#getBatteryDischargePrediction()} instead.
          * @hide
          */
+        @Deprecated
         public static final String TIME_REMAINING_ESTIMATE_MILLIS =
                 "time_remaining_estimate_millis";
 
         /**
-         * A boolean indicating whether {@link #TIME_REMAINING_ESTIMATE_MILLIS} is based customized
-         * to the devices usage or using global models. See
+         * A boolean indicating whether {@link #TIME_REMAINING_ESTIMATE_MILLIS} is customized
+         * to the device's usage or using global models. See
          * {@link #BATTERY_ESTIMATES_LAST_UPDATE_TIME} for the last time this value was updated.
          *
+         * @deprecated Use {@link PowerManager#isBatteryDischargePredictionPersonalized()} instead.
+         *
          * @hide
          */
+        @Deprecated
         public static final String TIME_REMAINING_ESTIMATE_BASED_ON_USAGE =
                 "time_remaining_estimate_based_on_usage";
 
@@ -12565,8 +12552,10 @@
          * average based on historical drain rates. See {@link #BATTERY_ESTIMATES_LAST_UPDATE_TIME}
          * for the last time this value was updated.
          *
+         * @deprecated Use {@link PowerManager#getHistoricalDischargeTime()} instead.
          * @hide
          */
+        @Deprecated
         public static final String AVERAGE_TIME_TO_DISCHARGE = "average_time_to_discharge";
 
         /**
@@ -12575,7 +12564,9 @@
          * and {@link #AVERAGE_TIME_TO_DISCHARGE} were last updated.
          *
          * @hide
+         * @deprecated No longer needed due to {@link PowerManager#getBatteryDischargePrediction}.
          */
+        @Deprecated
         public static final String BATTERY_ESTIMATES_LAST_UPDATE_TIME =
                 "battery_estimates_last_update_time";
 
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 678f43d..04a4ca4 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -137,7 +137,7 @@
  * <p>The service can provide an extra degree of security by requiring the user to authenticate
  * before an app can be autofilled. The authentication is typically required in 2 scenarios:
  * <ul>
- *   <li>To unlock the user data (for example, using a master password or fingerprint
+ *   <li>To unlock the user data (for example, using a main password or fingerprint
  *       authentication) - see
  * {@link FillResponse.Builder#setAuthentication(AutofillId[], android.content.IntentSender, android.widget.RemoteViews)}.
  *   <li>To unlock a specific dataset (for example, by providing a CVC for a credit card) - see
@@ -363,9 +363,9 @@
  * {@code login.some_bank.com} credentials to the {@code my_financial_app}; if the user agrees,
  * then the service returns an unlocked dataset with the {@code some_bank.com} credentials.
  *
- * <p><b>Note:</b> The autofill service could also whitelist well-known browser apps and skip the
- * verifications above, as long as the service can verify the authenticity of the browser app by
- * checking its signing certificate.
+ * <p><b>Note:</b> The autofill service could also add well-known browser apps into an allowlist and
+ * skip the verifications above, as long as the service can verify the authenticity of the browser
+ * app by checking its signing certificate.
  *
  * <a name="MultipleStepsSave"></a>
  * <h3>Saving when data is split in multiple screens</h3>
@@ -507,9 +507,9 @@
  * services and fill data. This mode needs to be explicitly requested for a given package up
  * to a specified max version code allowing clean migration path when the target app begins to
  * support autofill natively. Note that enabling compatibility may degrade performance for the
- * target package and should be used with caution. The platform supports whitelisting which packages
- * can be targeted in compatibility mode to ensure this mode is used only when needed and as long
- * as needed.
+ * target package and should be used with caution. The platform supports creating an allowlist for
+ * including which packages can be targeted in compatibility mode to ensure this mode is used only
+ * when needed and as long as needed.
  *
  * <p>You can request compatibility mode for packages of interest in the meta-data resource
  * associated with your service. Below is a sample service declaration:
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 3fcb361..3ed7f6c 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -62,17 +62,17 @@
  * to release the native resources used by the TextToSpeech engine.
  *
  * Apps targeting Android 11 that use text-to-speech should declare {@link
- * TextToSpeech.Engine#INTENT_ACTION_TTS_SERVICE} in the <code><queries></code> elements of their
+ * TextToSpeech.Engine#INTENT_ACTION_TTS_SERVICE} in the {@code queries} elements of their
  * manifest:
  *
- * <code>
- * <queries>
+ * <pre>
+ * &lt;queries&gt;
  *   ...
- *  <intent>
- *      <action android:name="android.intent.action.TTS_SERVICE" />
- *  </intent>
- * </queries>
- * </code>
+ *  &lt;intent&gt;
+ *      &lt;action android:name="android.intent.action.TTS_SERVICE" /&gt;
+ *  &lt;/intent&gt;
+ * &lt;/queries&gt;
+ * </pre>
  */
 public class TextToSpeech {
 
@@ -254,18 +254,17 @@
      * </ul>
      *
      * Apps targeting Android 11 that use text-to-speech should declare {@link
-     * #INTENT_ACTION_TTS_SERVICE} in the <code><queries></code> elements of their
+     * TextToSpeech.Engine#INTENT_ACTION_TTS_SERVICE} in the {@code queries} elements of their
      * manifest:
      *
-     * <code>
-     * <queries>
+     * <pre>
+     * &lt;queries&gt;
      *   ...
-     *  <intent>
-     *      <action android:name="android.intent.action.TTS_SERVICE" />
-     *  </intent>
-     * </queries>
-     * </code>
-
+     *  &lt;intent&gt;
+     *      &lt;action android:name="android.intent.action.TTS_SERVICE" /&gt;
+     *  &lt;/intent&gt;
+     * &lt;/queries&gt;
+     * </pre>
      */
     public class Engine {
 
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index a6e6d05..b807180 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -938,7 +938,6 @@
      *                           {@link SubscriptionManager#getDefaultSubscriptionId})
      *                           and the value as the list of {@link EmergencyNumber};
      *                           null if this information is not available.
-     * @hide
      */
     public void onEmergencyNumberListChanged(
             @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) {
@@ -948,17 +947,50 @@
     /**
      * Callback invoked when an outgoing call is placed to an emergency number.
      *
-     * @param placedEmergencyNumber the emergency number {@link EmergencyNumber} the call is placed
-     *                              to.
+     * This method will be called when an emergency call is placed on any subscription (including
+     * the no-SIM case), regardless of which subscription this listener was registered on.
+     *
+     * This method is deprecated. Both this method and the new
+     * {@link #onOutgoingEmergencyCall(EmergencyNumber, int)} will be called when an outgoing
+     * emergency call is placed.
+     *
+     * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was placed to.
+     *
+     * @deprecated Use {@link #onOutgoingEmergencyCall(EmergencyNumber, int)}.
      * @hide
      */
     @SystemApi
     @TestApi
+    @Deprecated
     public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) {
         // default implementation empty
     }
 
     /**
+     * Callback invoked when an outgoing call is placed to an emergency number.
+     *
+     * This method will be called when an emergency call is placed on any subscription (including
+     * the no-SIM case), regardless of which subscription this listener was registered on.
+     *
+     * Both this method and the deprecated {@link #onOutgoingEmergencyCall(EmergencyNumber)} will be
+     * called when an outgoing emergency call is placed. You should only implement one of these
+     * methods.
+     *
+     * @param placedEmergencyNumber The {@link EmergencyNumber} the emergency call was placed to.
+     * @param subscriptionId The subscription ID used to place the emergency call. If the
+     *                       emergency call was placed without a valid subscription (e.g. when there
+     *                       are no SIM cards in the device), this will be equal to
+     *                       {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
+            int subscriptionId) {
+    }
+
+    /**
      * Callback invoked when an outgoing SMS is placed to an emergency number.
      *
      * @param sentEmergencyNumber the emergency number {@link EmergencyNumber} the SMS is sent to.
@@ -1336,13 +1368,19 @@
                             () -> psl.onEmergencyNumberListChanged(emergencyNumberList)));
         }
 
-        public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) {
+        public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber,
+                int subscriptionId) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
 
             Binder.withCleanCallingIdentity(
                     () -> mExecutor.execute(
                             () -> psl.onOutgoingEmergencyCall(placedEmergencyNumber)));
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onOutgoingEmergencyCall(placedEmergencyNumber,
+                                    subscriptionId)));
         }
 
         public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) {
diff --git a/core/java/android/text/OWNERS b/core/java/android/text/OWNERS
index e561371..0b51b2d 100644
--- a/core/java/android/text/OWNERS
+++ b/core/java/android/text/OWNERS
@@ -1,5 +1,4 @@
 set noparent
 
 siyamed@google.com
-nona@google.com
-clarabayarri@google.com
+nona@google.com
\ No newline at end of file
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
index 6e34666..f74990a 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java
@@ -149,7 +149,7 @@
      * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2.
      * @throws IOException if an I/O error occurs while reading the APK file.
      */
-    private static SignatureInfo findSignature(RandomAccessFile apk)
+    public static SignatureInfo findSignature(RandomAccessFile apk)
             throws IOException, SignatureNotFoundException {
         return ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V2_BLOCK_ID);
     }
diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
index 9357285..5f963b0 100644
--- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
+++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java
@@ -142,7 +142,7 @@
      * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v3.
      * @throws IOException if an I/O error occurs while reading the APK file.
      */
-    private static SignatureInfo findSignature(RandomAccessFile apk)
+    public static SignatureInfo findSignature(RandomAccessFile apk)
             throws IOException, SignatureNotFoundException {
         return ApkSigningBlockUtils.findSignature(apk, APK_SIGNATURE_SCHEME_V3_BLOCK_ID);
     }
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index e0258f7..02edb7e 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -92,6 +92,20 @@
     private static PackageParser.SigningDetails verifySignatures(String apkPath,
             @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
             throws PackageParserException {
+        return verifySignaturesInternal(apkPath, minSignatureSchemeVersion,
+                verifyFull).signingDetails;
+    }
+
+    /**
+     * Verifies the provided APK using all allowed signing schemas.
+     * @return the certificates associated with each signer and content digests.
+     * @param verifyFull whether to verify all contents of this APK or just collect certificates.
+     * @throws PackageParserException if there was a problem collecting certificates
+     * @hide
+     */
+    public static SigningDetailsWithDigests verifySignaturesInternal(String apkPath,
+            @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
+            throws PackageParserException {
 
         if (minSignatureSchemeVersion > SignatureSchemeVersion.SIGNING_BLOCK_V4) {
             // V3 and before are older than the requested minimum signing version
@@ -121,7 +135,7 @@
         return verifyV3AndBelowSignatures(apkPath, minSignatureSchemeVersion, verifyFull);
     }
 
-    private static PackageParser.SigningDetails verifyV3AndBelowSignatures(String apkPath,
+    private static SigningDetailsWithDigests verifyV3AndBelowSignatures(String apkPath,
             @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
             throws PackageParserException {
         // try v3
@@ -174,7 +188,7 @@
      * @throws SignatureNotFoundException if there are no V4 signatures in the APK
      * @throws PackageParserException     if there was a problem collecting certificates
      */
-    private static PackageParser.SigningDetails verifyV4Signature(String apkPath,
+    private static SigningDetailsWithDigests verifyV4Signature(String apkPath,
             @SignatureSchemeVersion int minSignatureSchemeVersion, boolean verifyFull)
             throws SignatureNotFoundException, PackageParserException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV4" : "certsOnlyV4");
@@ -234,8 +248,8 @@
                 }
             }
 
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V4);
+            return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+                    SignatureSchemeVersion.SIGNING_BLOCK_V4), vSigner.contentDigests);
         } catch (SignatureNotFoundException e) {
             throw e;
         } catch (Exception e) {
@@ -256,8 +270,8 @@
      * @throws SignatureNotFoundException if there are no V3 signatures in the APK
      * @throws PackageParserException     if there was a problem collecting certificates
      */
-    private static PackageParser.SigningDetails verifyV3Signature(String apkPath,
-            boolean verifyFull) throws SignatureNotFoundException, PackageParserException {
+    private static SigningDetailsWithDigests verifyV3Signature(String apkPath, boolean verifyFull)
+            throws SignatureNotFoundException, PackageParserException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV3" : "certsOnlyV3");
         try {
             ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
@@ -275,8 +289,9 @@
                     pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
                 }
             }
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs);
+            return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+                    SignatureSchemeVersion.SIGNING_BLOCK_V3, pastSignerSigs),
+                    vSigner.contentDigests);
         } catch (SignatureNotFoundException e) {
             throw e;
         } catch (Exception e) {
@@ -297,15 +312,16 @@
      * @throws SignatureNotFoundException if there are no V2 signatures in the APK
      * @throws PackageParserException     if there was a problem collecting certificates
      */
-    private static PackageParser.SigningDetails verifyV2Signature(String apkPath,
-            boolean verifyFull) throws SignatureNotFoundException, PackageParserException {
+    private static SigningDetailsWithDigests verifyV2Signature(String apkPath, boolean verifyFull)
+            throws SignatureNotFoundException, PackageParserException {
         Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, verifyFull ? "verifyV2" : "certsOnlyV2");
         try {
-            Certificate[][] signerCerts = verifyFull ? ApkSignatureSchemeV2Verifier.verify(apkPath)
-                    : ApkSignatureSchemeV2Verifier.unsafeGetCertsWithoutVerification(apkPath);
+            ApkSignatureSchemeV2Verifier.VerifiedSigner vSigner =
+                    ApkSignatureSchemeV2Verifier.verify(apkPath, verifyFull);
+            Certificate[][] signerCerts = vSigner.certs;
             Signature[] signerSigs = convertToSignatures(signerCerts);
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V2);
+            return new SigningDetailsWithDigests(new PackageParser.SigningDetails(signerSigs,
+                    SignatureSchemeVersion.SIGNING_BLOCK_V2), vSigner.contentDigests);
         } catch (SignatureNotFoundException e) {
             throw e;
         } catch (Exception e) {
@@ -324,8 +340,7 @@
      * @param verifyFull whether to verify all contents of this APK or just collect certificates.
      * @throws PackageParserException if there was a problem collecting certificates
      */
-    private static PackageParser.SigningDetails verifyV1Signature(
-            String apkPath, boolean verifyFull)
+    private static SigningDetailsWithDigests verifyV1Signature(String apkPath, boolean verifyFull)
             throws PackageParserException {
         StrictJarFile jarFile = null;
 
@@ -391,7 +406,8 @@
                     }
                 }
             }
-            return new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR);
+            return new SigningDetailsWithDigests(
+                    new PackageParser.SigningDetails(lastSigs, SignatureSchemeVersion.JAR), null);
         } catch (GeneralSecurityException e) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
                     "Failed to collect certificates from " + apkPath, e);
@@ -542,4 +558,27 @@
             return null;
         }
     }
+
+    /**
+     * Extended signing details.
+     * @hide for internal use only.
+     */
+    public static class SigningDetailsWithDigests {
+        public final PackageParser.SigningDetails signingDetails;
+
+        /**
+         * APK Signature Schemes v2/v3/v4 might contain multiple content digests.
+         * SignatureVerifier usually chooses one of them to verify.
+         * For certain signature schemes, e.g. v4, this digest is verified continuously.
+         * For others, e.g. v2, the caller has to specify if they want to verify.
+         * Please refer to documentation for more details.
+         */
+        public final Map<Integer, byte[]> contentDigests;
+
+        SigningDetailsWithDigests(PackageParser.SigningDetails signingDetails,
+                Map<Integer, byte[]> contentDigests) {
+            this.signingDetails = signingDetails;
+            this.contentDigests = contentDigests;
+        }
+    }
 }
diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java
index 990092c..021f232 100644
--- a/core/java/android/util/apk/ApkSigningBlockUtils.java
+++ b/core/java/android/util/apk/ApkSigningBlockUtils.java
@@ -39,7 +39,7 @@
  *
  * @hide for internal use only.
  */
-final class ApkSigningBlockUtils {
+public final class ApkSigningBlockUtils {
 
     private ApkSigningBlockUtils() {
     }
@@ -146,6 +146,37 @@
             Map<Integer, byte[]> expectedDigests,
             FileDescriptor apkFileDescriptor,
             SignatureInfo signatureInfo) throws SecurityException {
+        int[] digestAlgorithms = new int[expectedDigests.size()];
+        int digestAlgorithmCount = 0;
+        for (int digestAlgorithm : expectedDigests.keySet()) {
+            digestAlgorithms[digestAlgorithmCount] = digestAlgorithm;
+            digestAlgorithmCount++;
+        }
+        byte[][] actualDigests;
+        try {
+            actualDigests = computeContentDigestsPer1MbChunk(digestAlgorithms, apkFileDescriptor,
+                    signatureInfo);
+        } catch (DigestException e) {
+            throw new SecurityException("Failed to compute digest(s) of contents", e);
+        }
+        for (int i = 0; i < digestAlgorithms.length; i++) {
+            int digestAlgorithm = digestAlgorithms[i];
+            byte[] expectedDigest = expectedDigests.get(digestAlgorithm);
+            byte[] actualDigest = actualDigests[i];
+            if (!MessageDigest.isEqual(expectedDigest, actualDigest)) {
+                throw new SecurityException(
+                        getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
+                                + " digest of contents did not verify");
+            }
+        }
+    }
+
+    /**
+     * Calculate digests using digestAlgorithms for apkFileDescriptor.
+     * This will skip signature block described by signatureInfo.
+     */
+    public static byte[][] computeContentDigestsPer1MbChunk(int[] digestAlgorithms,
+            FileDescriptor apkFileDescriptor, SignatureInfo signatureInfo) throws DigestException {
         // We need to verify the integrity of the following three sections of the file:
         // 1. Everything up to the start of the APK Signing Block.
         // 2. ZIP Central Directory.
@@ -156,6 +187,7 @@
         // avoid wasting physical memory. In most APK verification scenarios, the contents of the
         // APK are already there in the OS's page cache and thus mmap does not use additional
         // physical memory.
+
         DataSource beforeApkSigningBlock =
                 new MemoryMappedFileDataSource(apkFileDescriptor, 0,
                         signatureInfo.apkSigningBlockOffset);
@@ -171,31 +203,8 @@
         ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, signatureInfo.apkSigningBlockOffset);
         DataSource eocd = new ByteBufferDataSource(eocdBuf);
 
-        int[] digestAlgorithms = new int[expectedDigests.size()];
-        int digestAlgorithmCount = 0;
-        for (int digestAlgorithm : expectedDigests.keySet()) {
-            digestAlgorithms[digestAlgorithmCount] = digestAlgorithm;
-            digestAlgorithmCount++;
-        }
-        byte[][] actualDigests;
-        try {
-            actualDigests =
-                    computeContentDigestsPer1MbChunk(
-                            digestAlgorithms,
-                            new DataSource[] {beforeApkSigningBlock, centralDir, eocd});
-        } catch (DigestException e) {
-            throw new SecurityException("Failed to compute digest(s) of contents", e);
-        }
-        for (int i = 0; i < digestAlgorithms.length; i++) {
-            int digestAlgorithm = digestAlgorithms[i];
-            byte[] expectedDigest = expectedDigests.get(digestAlgorithm);
-            byte[] actualDigest = actualDigests[i];
-            if (!MessageDigest.isEqual(expectedDigest, actualDigest)) {
-                throw new SecurityException(
-                        getContentDigestAlgorithmJcaDigestAlgorithm(digestAlgorithm)
-                                + " digest of contents did not verify");
-            }
-        }
+        return computeContentDigestsPer1MbChunk(digestAlgorithms,
+                new DataSource[]{beforeApkSigningBlock, centralDir, eocd});
     }
 
     private static byte[][] computeContentDigestsPer1MbChunk(
@@ -417,14 +426,10 @@
     static final int SIGNATURE_VERITY_ECDSA_WITH_SHA256 = 0x0423;
     static final int SIGNATURE_VERITY_DSA_WITH_SHA256 = 0x0425;
 
-    static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
-    static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
-    static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3;
-    static final int CONTENT_DIGEST_SHA256 = 4;
-
-    private static final int[] V4_CONTENT_DIGEST_ALGORITHMS =
-            {CONTENT_DIGEST_CHUNKED_SHA512, CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
-                    CONTENT_DIGEST_CHUNKED_SHA256};
+    public static final int CONTENT_DIGEST_CHUNKED_SHA256 = 1;
+    public static final int CONTENT_DIGEST_CHUNKED_SHA512 = 2;
+    public static final int CONTENT_DIGEST_VERITY_CHUNKED_SHA256 = 3;
+    public static final int CONTENT_DIGEST_SHA256 = 4;
 
     static int compareSignatureAlgorithm(int sigAlgorithm1, int sigAlgorithm2) {
         int digestAlgorithm1 = getSignatureAlgorithmContentDigestAlgorithm(sigAlgorithm1);
diff --git a/core/java/android/util/apk/SignatureInfo.java b/core/java/android/util/apk/SignatureInfo.java
index 8e1233a..7638293 100644
--- a/core/java/android/util/apk/SignatureInfo.java
+++ b/core/java/android/util/apk/SignatureInfo.java
@@ -16,15 +16,18 @@
 
 package android.util.apk;
 
+import android.annotation.NonNull;
+
 import java.nio.ByteBuffer;
 
 /**
  * APK Signature Scheme v2 block and additional information relevant to verifying the signatures
  * contained in the block against the file.
+ * @hide
  */
-class SignatureInfo {
+public class SignatureInfo {
     /** Contents of APK Signature Scheme v2 block. */
-    public final ByteBuffer signatureBlock;
+    public final @NonNull ByteBuffer signatureBlock;
 
     /** Position of the APK Signing Block in the file. */
     public final long apkSigningBlockOffset;
@@ -36,10 +39,10 @@
     public final long eocdOffset;
 
     /** Contents of ZIP End of Central Directory (EoCD) of the file. */
-    public final ByteBuffer eocd;
+    public final @NonNull ByteBuffer eocd;
 
-    SignatureInfo(ByteBuffer signatureBlock, long apkSigningBlockOffset, long centralDirOffset,
-            long eocdOffset, ByteBuffer eocd) {
+    SignatureInfo(@NonNull ByteBuffer signatureBlock, long apkSigningBlockOffset,
+            long centralDirOffset, long eocdOffset, @NonNull ByteBuffer eocd) {
         this.signatureBlock = signatureBlock;
         this.apkSigningBlockOffset = apkSigningBlockOffset;
         this.centralDirOffset = centralDirOffset;
diff --git a/core/java/android/util/apk/VerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java
index e81e3f7..4596c6e 100644
--- a/core/java/android/util/apk/VerityBuilder.java
+++ b/core/java/android/util/apk/VerityBuilder.java
@@ -116,6 +116,34 @@
     }
 
     /**
+     * Generates the fs-verity hash tree. It is the actual verity tree format on disk, as is
+     * re-generated on device.
+     *
+     * The tree is built bottom up. The bottom level has 256-bit digest for each 4 KB block in the
+     * input file.  If the total size is larger than 4 KB, take this level as input and repeat the
+     * same procedure, until the level is within 4 KB.  If salt is given, it will apply to each
+     * digestion before the actual data.
+     *
+     * The returned root hash is calculated from the last level of 4 KB chunk, similarly with salt.
+     *
+     * @return the root hash of the generated hash tree.
+     */
+    public static byte[] generateFsVerityRootHash(@NonNull String apkPath, byte[] salt,
+            @NonNull ByteBufferFactory bufferFactory)
+            throws IOException, NoSuchAlgorithmException, DigestException {
+        try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {
+            int[] levelOffset = calculateVerityLevelOffset(apk.length());
+            int merkleTreeSize = levelOffset[levelOffset.length - 1];
+
+            ByteBuffer output = bufferFactory.create(
+                    merkleTreeSize
+                            + CHUNK_SIZE_BYTES);  // maximum size of apk-verity metadata
+            output.order(ByteOrder.LITTLE_ENDIAN);
+            ByteBuffer tree = slice(output, 0, merkleTreeSize);
+            return generateFsVerityTreeInternal(apk, salt, levelOffset, tree);
+        }
+    }
+    /**
      * Calculates the apk-verity root hash for integrity measurement.  This needs to be consistent
      * to what kernel returns.
      */
@@ -259,9 +287,10 @@
     // thus the syscall overhead is not too big.
     private static final int MMAP_REGION_SIZE_BYTES = 1024 * 1024;
 
-    private static void generateFsVerityDigestAtLeafLevel(RandomAccessFile file, ByteBuffer output)
+    private static void generateFsVerityDigestAtLeafLevel(RandomAccessFile file,
+            @Nullable byte[] salt, ByteBuffer output)
             throws IOException, NoSuchAlgorithmException, DigestException {
-        BufferedDigester digester = new BufferedDigester(null /* salt */, output);
+        BufferedDigester digester = new BufferedDigester(salt, output);
 
         // 1. Digest the whole file by chunks.
         consumeByChunk(digester,
@@ -325,6 +354,35 @@
     }
 
     @NonNull
+    private static byte[] generateFsVerityTreeInternal(@NonNull RandomAccessFile apk,
+            @Nullable byte[] salt, @NonNull int[] levelOffset, @NonNull ByteBuffer output)
+            throws IOException, NoSuchAlgorithmException, DigestException {
+        // 1. Digest the apk to generate the leaf level hashes.
+        generateFsVerityDigestAtLeafLevel(apk, salt,
+                slice(output, levelOffset[levelOffset.length - 2],
+                        levelOffset[levelOffset.length - 1]));
+
+        // 2. Digest the lower level hashes bottom up.
+        for (int level = levelOffset.length - 3; level >= 0; level--) {
+            ByteBuffer inputBuffer = slice(output, levelOffset[level + 1], levelOffset[level + 2]);
+            ByteBuffer outputBuffer = slice(output, levelOffset[level], levelOffset[level + 1]);
+
+            DataSource source = new ByteBufferDataSource(inputBuffer);
+            BufferedDigester digester = new BufferedDigester(salt, outputBuffer);
+            consumeByChunk(digester, source, CHUNK_SIZE_BYTES);
+            digester.assertEmptyBuffer();
+            digester.fillUpLastOutputChunk();
+        }
+
+        // 3. Digest the first block (i.e. first level) to generate the root hash.
+        byte[] rootHash = new byte[DIGEST_SIZE_BYTES];
+        BufferedDigester digester = new BufferedDigester(salt, ByteBuffer.wrap(rootHash));
+        digester.consume(slice(output, 0, CHUNK_SIZE_BYTES));
+        digester.assertEmptyBuffer();
+        return rootHash;
+    }
+
+    @NonNull
     private static byte[] generateVerityTreeInternal(@NonNull RandomAccessFile apk,
             @Nullable SignatureInfo signatureInfo, @Nullable byte[] salt,
             @NonNull int[] levelOffset, @NonNull ByteBuffer output)
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 25a4108..a06a0c6 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -25,6 +25,7 @@
 
 import dalvik.system.CloseGuard;
 
+import java.io.PrintWriter;
 import java.lang.ref.Reference;
 import java.lang.ref.WeakReference;
 
@@ -53,6 +54,7 @@
     private static native void nativeFinishInputEvent(long receiverPtr, int seq, boolean handled);
     private static native boolean nativeConsumeBatchedInputEvents(long receiverPtr,
             long frameTimeNanos);
+    private static native String nativeDump(long receiverPtr, String prefix);
 
     /**
      * Creates an input event receiver bound to the specified input channel.
@@ -221,6 +223,18 @@
     }
 
     /**
+     * Dump the state of this InputEventReceiver to the writer.
+     * @param prefix the prefix (typically whitespace padding) to append in front of each line
+     * @param writer the writer where the dump should be written
+     */
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(prefix + getClass().getName());
+        writer.println(prefix + " mInputChannel: " + mInputChannel);
+        writer.println(prefix + " mSeqMap: " + mSeqMap);
+        writer.println(prefix + " mReceiverPtr:\n" + nativeDump(mReceiverPtr, prefix + "  "));
+    }
+
+    /**
      * Factory for InputEventReceiver
      */
     public interface Factory {
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index e341845..1ef701f 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -70,11 +70,8 @@
     // Window is visible.
     public boolean visible;
 
-    // Window can receive keys.
-    public boolean canReceiveKeys;
-
-    // Window has focus.
-    public boolean hasFocus;
+    // Window can be focused.
+    public boolean focusable;
 
     // Window has wallpaper.  (window is the current wallpaper target)
     public boolean hasWallpaper;
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 6136a80..0c3d61f 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -456,8 +456,8 @@
                 case MotionEvent.ACTION_UP:
                     if (mTrackGesture) {
                         if (mFeedbackIcon.isVisibleToUser()
-                                && (mFeedbackRect.contains((int) x, (int) y))
-                                || mFeedbackRect.contains((int) mDownX, (int) mDownY)) {
+                                && (mFeedbackRect.contains((int) x, (int) y)
+                                || mFeedbackRect.contains((int) mDownX, (int) mDownY))) {
                             mFeedbackIcon.performClick();
                             return true;
                         }
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 859e9a4..65cc2f8 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator;
+import android.annotation.FloatRange;
 import android.graphics.RenderNode;
 
 import java.util.ArrayList;
@@ -725,7 +726,7 @@
      * @see View#setAlpha(float)
      * @return This object, allowing calls to methods in this class to be chained.
      */
-    public ViewPropertyAnimator alpha(float value) {
+    public ViewPropertyAnimator alpha(@FloatRange(from = 0.0f, to = 1.0f) float value) {
         animateProperty(ALPHA, value);
         return this;
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3f02d70..6e17ac9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -7532,36 +7532,36 @@
 
     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
         String innerPrefix = prefix + "  ";
-        writer.print(prefix); writer.println("ViewRoot:");
-        writer.print(innerPrefix); writer.print("mAdded="); writer.print(mAdded);
-                writer.print(" mRemoved="); writer.println(mRemoved);
-        writer.print(innerPrefix); writer.print("mConsumeBatchedInputScheduled=");
-                writer.println(mConsumeBatchedInputScheduled);
-        writer.print(innerPrefix); writer.print("mConsumeBatchedInputImmediatelyScheduled=");
-                writer.println(mConsumeBatchedInputImmediatelyScheduled);
-        writer.print(innerPrefix); writer.print("mPendingInputEventCount=");
-                writer.println(mPendingInputEventCount);
-        writer.print(innerPrefix); writer.print("mProcessInputEventsScheduled=");
-                writer.println(mProcessInputEventsScheduled);
-        writer.print(innerPrefix); writer.print("mTraversalScheduled=");
-                writer.print(mTraversalScheduled);
-        writer.print(innerPrefix); writer.print("mIsAmbientMode=");
-                writer.print(mIsAmbientMode);
-        writer.print(innerPrefix); writer.print("mUnbufferedInputSource=");
-        writer.print(Integer.toHexString(mUnbufferedInputSource));
-
+        writer.println(prefix + "ViewRoot:");
+        writer.println(innerPrefix + "mAdded=" + mAdded);
+        writer.println(innerPrefix + "mRemoved=" + mRemoved);
+        writer.println(innerPrefix + "mStopped=" + mStopped);
+        writer.println(innerPrefix + "mConsumeBatchedInputScheduled="
+                + mConsumeBatchedInputScheduled);
+        writer.println(innerPrefix + "mConsumeBatchedInputImmediatelyScheduled="
+                + mConsumeBatchedInputImmediatelyScheduled);
+        writer.println(innerPrefix + "mPendingInputEventCount=" + mPendingInputEventCount);
+        writer.println(innerPrefix + "mProcessInputEventsScheduled="
+                + mProcessInputEventsScheduled);
+        writer.println(innerPrefix + "mTraversalScheduled=" + mTraversalScheduled);
         if (mTraversalScheduled) {
-            writer.print(" (barrier="); writer.print(mTraversalBarrier); writer.println(")");
-        } else {
-            writer.println();
+            writer.println(innerPrefix + " (barrier=" + mTraversalBarrier + ")");
         }
+        writer.println(innerPrefix + "mIsAmbientMode="  + mIsAmbientMode);
+        writer.println(innerPrefix + "mUnbufferedInputSource="
+                + Integer.toHexString(mUnbufferedInputSource));
+
         mFirstInputStage.dump(innerPrefix, writer);
 
+        if (mInputEventReceiver != null) {
+            mInputEventReceiver.dump(innerPrefix, writer);
+        }
+
         mChoreographer.dump(prefix, writer);
 
         mInsetsController.dump(prefix, writer);
 
-        writer.print(prefix); writer.println("View Hierarchy:");
+        writer.println(prefix + "View Hierarchy:");
         dumpViewHierarchy(innerPrefix, writer, mView);
     }
 
@@ -8890,6 +8890,9 @@
      * @param targets the search queue for targets
      */
     private void collectRootScrollCaptureTargets(Queue<ScrollCaptureTarget> targets) {
+        if (mRootScrollCaptureCallbacks == null) {
+            return;
+        }
         for (ScrollCaptureCallback cb : mRootScrollCaptureCallbacks) {
             // Add to the list for consideration
             Point offset = new Point(mView.getLeft(), mView.getTop());
diff --git a/core/java/android/webkit/PacProcessor.java b/core/java/android/webkit/PacProcessor.java
index 7e7b987..b04105a 100644
--- a/core/java/android/webkit/PacProcessor.java
+++ b/core/java/android/webkit/PacProcessor.java
@@ -32,6 +32,10 @@
     /**
      * Returns the default PacProcessor instance.
      *
+     * <p> There can only be one default {@link PacProcessor} instance.
+     * This method will create a new instance if one did not already exist, or
+     * if the previous instance was released with {@link #releasePacProcessor}.
+     *
      * @return the default PacProcessor instance.
      */
     @NonNull
@@ -43,14 +47,21 @@
      * Returns PacProcessor instance associated with the {@link Network}.
      * The host resolution is done on this {@link Network}.
      *
-     * @param networkHandle a handle representing {@link Network} handle.
-     * @return PacProcessor instance for the specified network.
-     * @see Network#getNetworkHandle
-     * @see Network#fromNetworkHandle
+     * <p> There can only be one {@link PacProcessor} instance at a time for each {@link Network}.
+     * This method will create a new instance if one did not already exist, or
+     * if the previous instance was released with {@link #releasePacProcessor}.
+     *
+     * <p> The {@link PacProcessor} instance needs to be released manually with
+     * {@link #releasePacProcessor} when the associated {@link Network} goes away.
+     *
+     * @param network a {@link Network} which this {@link PacProcessor}
+     * will use for host/address resolution.
+     * If {@code null} this method is equivalent to {@link #getInstance}.
+     * @return {@link PacProcessor} instance for the specified network.
      */
     @NonNull
-    static PacProcessor getInstanceForNetwork(long networkHandle) {
-        return WebViewFactory.getProvider().getPacProcessorForNetwork(networkHandle);
+    static PacProcessor getInstanceForNetwork(@Nullable Network network) {
+        return WebViewFactory.getProvider().getPacProcessorForNetwork(network);
     }
 
     /**
@@ -73,19 +84,22 @@
     /**
      * Stops support for this {@link PacProcessor} and release its resources.
      * No methods of this class must be called after calling this method.
+     *
+     * <p> Released instances will not be reused; a subsequent call to
+     * {@link #getInstance} and {@link #getInstanceForNetwork}
+     * for the same network will create a new instance.
      */
     default void releasePacProcessor() {
         throw new UnsupportedOperationException("Not implemented");
     }
 
     /**
-     * Returns a network handle associated with this {@link PacProcessor}.
+     * Returns a {@link Network} associated with this {@link PacProcessor}.
      *
-     * @return a network handle or 0 if a network is unspecified.
-     * @see Network#getNetworkHandle
-     * @see Network#fromNetworkHandle
+     * @return an associated {@link Network} or {@code null} if a network is unspecified.
      */
-    default long getNetworkHandle() {
+    @Nullable
+    default Network getNetwork() {
         throw new UnsupportedOperationException("Not implemented");
     }
 }
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index f1863e3..ce999cd 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -17,6 +17,7 @@
 package android.webkit;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
@@ -188,13 +189,13 @@
      * Returns PacProcessor instance associated with the {@link Network}.
      * The host resolution is done on this {@link Network}.
      *
-     * @param networkHandle a network handle representing the {@link Network}.
+     * @param network a {@link Network} which needs to be associated
+     * with the returned {@link PacProcessor}.
+     * If {@code null} the method returns default {@link PacProcessor}.
      * @return the {@link PacProcessor} instance associated with {@link Network}.
-     * @see Network#getNetworkHandle
-     * @see Network#fromNetworkHandle
      */
     @NonNull
-    default PacProcessor getPacProcessorForNetwork(long networkHandle) {
+    default PacProcessor getPacProcessorForNetwork(@Nullable Network network) {
         throw new UnsupportedOperationException("Not implemented");
     }
 
diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl
index 1c03b2f..92fa80e 100644
--- a/core/java/android/window/ITaskOrganizerController.aidl
+++ b/core/java/android/window/ITaskOrganizerController.aidl
@@ -25,11 +25,9 @@
 interface ITaskOrganizerController {
 
     /**
-     * Register a TaskOrganizer to manage tasks as they enter the given windowing mode.
-     * If there was already a TaskOrganizer for this windowing mode it will be evicted
-     * and receive taskVanished callbacks in the process.
+     * Register a TaskOrganizer to manage all the tasks with supported windowing modes.
      */
-    void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode);
+    void registerTaskOrganizer(ITaskOrganizer organizer);
 
     /**
      * Unregisters a previously registered task organizer.
diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java
index 502680d..7ec4f99 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -36,14 +36,12 @@
 public class TaskOrganizer extends WindowOrganizer {
 
     /**
-     * Register a TaskOrganizer to manage tasks as they enter the given windowing mode.
-     * If there was already a TaskOrganizer for this windowing mode it will be evicted
-     * and receive taskVanished callbacks in the process.
+     * Register a TaskOrganizer to manage tasks as they enter a supported windowing mode.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public final void registerOrganizer(int windowingMode) {
+    public final void registerOrganizer() {
         try {
-            getController().registerTaskOrganizer(mInterface, windowingMode);
+            getController().registerTaskOrganizer(mInterface);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/window/TaskOrganizerTaskEmbedder.java b/core/java/android/window/TaskOrganizerTaskEmbedder.java
index 1b87521..46c72f8 100644
--- a/core/java/android/window/TaskOrganizerTaskEmbedder.java
+++ b/core/java/android/window/TaskOrganizerTaskEmbedder.java
@@ -73,7 +73,7 @@
         // TODO(wm-shell): This currently prevents other organizers from controlling MULT_WINDOW
         // windowing mode tasks. Plan is to migrate this to a wm-shell front-end when that
         // infrastructure is ready.
-        mTaskOrganizer.registerOrganizer(WINDOWING_MODE_MULTI_WINDOW);
+        // mTaskOrganizer.registerOrganizer();
         mTaskOrganizer.setInterceptBackPressedOnTaskRoot(true);
 
         return super.onInitialize();
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index eb59f0f..da26930 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -409,6 +409,11 @@
      */
     public static final String BACK_GESTURE_SLOP_MULTIPLIER = "back_gesture_slop_multiplier";
 
+    /**
+     * (long) Screenshot keychord delay (how long the buttons must be pressed), in ms
+     */
+    public static final String SCREENSHOT_KEYCHORD_DELAY = "screenshot_keychord_delay";
+
     private SystemUiDeviceConfigFlags() {
     }
 }
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index a50a522..3b5fecf 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -113,6 +113,14 @@
         // Default is no-op
     }
 
+    /**
+     * Callback indicating that the given document has been deleted or moved. This gives
+     * the provider a hook to revoke the uri permissions.
+     */
+    protected void onDocIdDeleted(String docId) {
+        // Default is no-op
+    }
+
     @Override
     public boolean onCreate() {
         throw new UnsupportedOperationException(
@@ -283,6 +291,7 @@
 
         final String afterDocId = getDocIdForFile(after);
         onDocIdChanged(docId);
+        onDocIdDeleted(docId);
         onDocIdChanged(afterDocId);
 
         final File afterVisibleFile = getFileForDocId(afterDocId, true);
@@ -312,6 +321,7 @@
 
         final String docId = getDocIdForFile(after);
         onDocIdChanged(sourceDocumentId);
+        onDocIdDeleted(sourceDocumentId);
         onDocIdChanged(docId);
         moveInMediaStore(visibleFileBefore, getFileForDocId(docId, true));
 
@@ -343,6 +353,7 @@
         }
 
         onDocIdChanged(docId);
+        onDocIdDeleted(docId);
         removeFromMediaStore(visibleFile);
     }
 
diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING
index f44b9fb..9698f19 100644
--- a/core/java/com/android/internal/os/TEST_MAPPING
+++ b/core/java/com/android/internal/os/TEST_MAPPING
@@ -26,5 +26,20 @@
         "KernelSingleUidTimeReader\\.java"
       ]
     }
+  ],
+  "postsubmit": [
+    {
+      "name": "FrameworksCoreTests",
+      "options": [
+        {
+          "include-filter": "com.android.internal.os.BstatsCpuTimesValidationTest"
+        }
+      ],
+      "file_patterns": [
+        "BatteryStatsImpl\\.java",
+        "KernelCpuUidFreqTimeReader\\.java",
+        "KernelSingleUidTimeReader\\.java"
+      ]
+    }
   ]
 }
diff --git a/core/java/com/android/internal/protolog/BaseProtoLogImpl.java b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
new file mode 100644
index 0000000..8a4eb4a
--- /dev/null
+++ b/core/java/com/android/internal/protolog/BaseProtoLogImpl.java
@@ -0,0 +1,397 @@
+/*
+ * 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.internal.protolog;
+
+import static com.android.internal.protolog.ProtoLogFileProto.LOG;
+import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER;
+import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_H;
+import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_L;
+import static com.android.internal.protolog.ProtoLogFileProto.REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS;
+import static com.android.internal.protolog.ProtoLogFileProto.VERSION;
+import static com.android.internal.protolog.ProtoLogMessage.BOOLEAN_PARAMS;
+import static com.android.internal.protolog.ProtoLogMessage.DOUBLE_PARAMS;
+import static com.android.internal.protolog.ProtoLogMessage.ELAPSED_REALTIME_NANOS;
+import static com.android.internal.protolog.ProtoLogMessage.MESSAGE_HASH;
+import static com.android.internal.protolog.ProtoLogMessage.SINT64_PARAMS;
+import static com.android.internal.protolog.ProtoLogMessage.STR_PARAMS;
+
+import android.annotation.Nullable;
+import android.os.ShellCommand;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.internal.protolog.common.LogDataType;
+import com.android.internal.util.TraceBuffer;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.IllegalFormatConversionException;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+
+
+/**
+ * A service for the ProtoLog logging system.
+ */
+public class BaseProtoLogImpl {
+    protected static final TreeMap<String, IProtoLogGroup> LOG_GROUPS = new TreeMap<>();
+
+    /**
+     * A runnable to update the cached output of {@link #isEnabled}.
+     *
+     * Must be invoked after every action that could change the result of {@link #isEnabled}, eg.
+     * starting / stopping proto log, or enabling / disabling log groups.
+     */
+    public static Runnable sCacheUpdater = () -> { };
+
+    protected static void addLogGroupEnum(IProtoLogGroup[] config) {
+        for (IProtoLogGroup group : config) {
+            LOG_GROUPS.put(group.name(), group);
+        }
+    }
+
+    private static final String TAG = "ProtoLog";
+    private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
+    static final String PROTOLOG_VERSION = "1.0.0";
+
+    private final File mLogFile;
+    private final String mViewerConfigFilename;
+    private final TraceBuffer mBuffer;
+    protected final ProtoLogViewerConfigReader mViewerConfig;
+
+    private boolean mProtoLogEnabled;
+    private boolean mProtoLogEnabledLockFree;
+    private final Object mProtoLogEnabledLock = new Object();
+
+    @VisibleForTesting
+    public enum LogLevel {
+        DEBUG, VERBOSE, INFO, WARN, ERROR, WTF
+    }
+
+    /**
+     * Main log method, do not call directly.
+     */
+    @VisibleForTesting
+    public void log(LogLevel level, IProtoLogGroup group, int messageHash, int paramsMask,
+            @Nullable String messageString, Object[] args) {
+        if (group.isLogToProto()) {
+            logToProto(messageHash, paramsMask, args);
+        }
+        if (group.isLogToLogcat()) {
+            logToLogcat(group.getTag(), level, messageHash, messageString, args);
+        }
+    }
+
+    private void logToLogcat(String tag, LogLevel level, int messageHash,
+            @Nullable String messageString, Object[] args) {
+        String message = null;
+        if (messageString == null) {
+            messageString = mViewerConfig.getViewerString(messageHash);
+        }
+        if (messageString != null) {
+            try {
+                message = String.format(messageString, args);
+            } catch (IllegalFormatConversionException ex) {
+                Slog.w(TAG, "Invalid ProtoLog format string.", ex);
+            }
+        }
+        if (message == null) {
+            StringBuilder builder = new StringBuilder("UNKNOWN MESSAGE (" + messageHash + ")");
+            for (Object o : args) {
+                builder.append(" ").append(o);
+            }
+            message = builder.toString();
+        }
+        passToLogcat(tag, level, message);
+    }
+
+    /**
+     * SLog wrapper.
+     */
+    @VisibleForTesting
+    public void passToLogcat(String tag, LogLevel level, String message) {
+        switch (level) {
+            case DEBUG:
+                Slog.d(tag, message);
+                break;
+            case VERBOSE:
+                Slog.v(tag, message);
+                break;
+            case INFO:
+                Slog.i(tag, message);
+                break;
+            case WARN:
+                Slog.w(tag, message);
+                break;
+            case ERROR:
+                Slog.e(tag, message);
+                break;
+            case WTF:
+                Slog.wtf(tag, message);
+                break;
+        }
+    }
+
+    private void logToProto(int messageHash, int paramsMask, Object[] args) {
+        if (!isProtoEnabled()) {
+            return;
+        }
+        try {
+            ProtoOutputStream os = new ProtoOutputStream();
+            long token = os.start(LOG);
+            os.write(MESSAGE_HASH, messageHash);
+            os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
+
+            if (args != null) {
+                int argIndex = 0;
+                ArrayList<Long> longParams = new ArrayList<>();
+                ArrayList<Double> doubleParams = new ArrayList<>();
+                ArrayList<Boolean> booleanParams = new ArrayList<>();
+                for (Object o : args) {
+                    int type = LogDataType.bitmaskToLogDataType(paramsMask, argIndex);
+                    try {
+                        switch (type) {
+                            case LogDataType.STRING:
+                                os.write(STR_PARAMS, o.toString());
+                                break;
+                            case LogDataType.LONG:
+                                longParams.add(((Number) o).longValue());
+                                break;
+                            case LogDataType.DOUBLE:
+                                doubleParams.add(((Number) o).doubleValue());
+                                break;
+                            case LogDataType.BOOLEAN:
+                                booleanParams.add((boolean) o);
+                                break;
+                        }
+                    } catch (ClassCastException ex) {
+                        // Should not happen unless there is an error in the ProtoLogTool.
+                        os.write(STR_PARAMS, "(INVALID PARAMS_MASK) " + o.toString());
+                        Slog.e(TAG, "Invalid ProtoLog paramsMask", ex);
+                    }
+                    argIndex++;
+                }
+                if (longParams.size() > 0) {
+                    os.writePackedSInt64(SINT64_PARAMS,
+                            longParams.stream().mapToLong(i -> i).toArray());
+                }
+                if (doubleParams.size() > 0) {
+                    os.writePackedDouble(DOUBLE_PARAMS,
+                            doubleParams.stream().mapToDouble(i -> i).toArray());
+                }
+                if (booleanParams.size() > 0) {
+                    boolean[] arr = new boolean[booleanParams.size()];
+                    for (int i = 0; i < booleanParams.size(); i++) {
+                        arr[i] = booleanParams.get(i);
+                    }
+                    os.writePackedBool(BOOLEAN_PARAMS, arr);
+                }
+            }
+            os.end(token);
+            mBuffer.add(os);
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception while logging to proto", e);
+        }
+    }
+
+    public BaseProtoLogImpl(File file, String viewerConfigFilename, int bufferCapacity,
+            ProtoLogViewerConfigReader viewerConfig) {
+        mLogFile = file;
+        mBuffer = new TraceBuffer(bufferCapacity);
+        mViewerConfigFilename = viewerConfigFilename;
+        mViewerConfig = viewerConfig;
+    }
+
+    /**
+     * Starts the logging a circular proto buffer.
+     *
+     * @param pw Print writer
+     */
+    public void startProtoLog(@Nullable PrintWriter pw) {
+        if (isProtoEnabled()) {
+            return;
+        }
+        synchronized (mProtoLogEnabledLock) {
+            logAndPrintln(pw, "Start logging to " + mLogFile + ".");
+            mBuffer.resetBuffer();
+            mProtoLogEnabled = true;
+            mProtoLogEnabledLockFree = true;
+        }
+        sCacheUpdater.run();
+    }
+
+    /**
+     * Stops logging to proto.
+     *
+     * @param pw          Print writer
+     * @param writeToFile If the current buffer should be written to disk or not
+     */
+    public void stopProtoLog(@Nullable PrintWriter pw, boolean writeToFile) {
+        if (!isProtoEnabled()) {
+            return;
+        }
+        synchronized (mProtoLogEnabledLock) {
+            logAndPrintln(pw, "Stop logging to " + mLogFile + ". Waiting for log to flush.");
+            mProtoLogEnabled = mProtoLogEnabledLockFree = false;
+            if (writeToFile) {
+                writeProtoLogToFileLocked();
+                logAndPrintln(pw, "Log written to " + mLogFile + ".");
+            }
+            if (mProtoLogEnabled) {
+                logAndPrintln(pw, "ERROR: logging was re-enabled while waiting for flush.");
+                throw new IllegalStateException("logging enabled while waiting for flush.");
+            }
+        }
+        sCacheUpdater.run();
+    }
+
+    /**
+     * Returns {@code true} iff logging to proto is enabled.
+     */
+    public boolean isProtoEnabled() {
+        return mProtoLogEnabledLockFree;
+    }
+
+    protected int setLogging(boolean setTextLogging, boolean value, PrintWriter pw,
+            String... groups) {
+        for (int i = 0; i < groups.length; i++) {
+            String group = groups[i];
+            IProtoLogGroup g = LOG_GROUPS.get(group);
+            if (g != null) {
+                System.out.println("G: "+ g);
+                if (setTextLogging) {
+                    g.setLogToLogcat(value);
+                } else {
+                    g.setLogToProto(value);
+                }
+            } else {
+                logAndPrintln(pw, "No IProtoLogGroup named " + group);
+                return -1;
+            }
+        }
+        sCacheUpdater.run();
+        return 0;
+    }
+
+    private int unknownCommand(PrintWriter pw) {
+        pw.println("Unknown command");
+        pw.println("Window manager logging options:");
+        pw.println("  start: Start proto logging");
+        pw.println("  stop: Stop proto logging");
+        pw.println("  enable [group...]: Enable proto logging for given groups");
+        pw.println("  disable [group...]: Disable proto logging for given groups");
+        pw.println("  enable-text [group...]: Enable logcat logging for given groups");
+        pw.println("  disable-text [group...]: Disable logcat logging for given groups");
+        return -1;
+    }
+
+    /**
+     * Responds to a shell command.
+     */
+    public int onShellCommand(ShellCommand shell) {
+        PrintWriter pw = shell.getOutPrintWriter();
+        String cmd = shell.getNextArg();
+        if (cmd == null) {
+            return unknownCommand(pw);
+        }
+        ArrayList<String> args = new ArrayList<>();
+        String arg;
+        while ((arg = shell.getNextArg()) != null) {
+            args.add(arg);
+        }
+        String[] groups = args.toArray(new String[args.size()]);
+        switch (cmd) {
+            case "start":
+                startProtoLog(pw);
+                return 0;
+            case "stop":
+                stopProtoLog(pw, true);
+                return 0;
+            case "status":
+                logAndPrintln(pw, getStatus());
+                return 0;
+            case "enable":
+                return setLogging(false, true, pw, groups);
+            case "enable-text":
+                mViewerConfig.loadViewerConfig(pw, mViewerConfigFilename);
+                return setLogging(true, true, pw, groups);
+            case "disable":
+                return setLogging(false, false, pw, groups);
+            case "disable-text":
+                return setLogging(true, false, pw, groups);
+            default:
+                return unknownCommand(pw);
+        }
+    }
+
+    /**
+     * Returns a human-readable ProtoLog status text.
+     */
+    public String getStatus() {
+        return "ProtoLog status: "
+                + ((isProtoEnabled()) ? "Enabled" : "Disabled")
+                + "\nEnabled log groups: \n  Proto: "
+                + LOG_GROUPS.values().stream().filter(
+                    it -> it.isEnabled() && it.isLogToProto())
+                .map(IProtoLogGroup::name).collect(Collectors.joining(" "))
+                + "\n  Logcat: "
+                + LOG_GROUPS.values().stream().filter(
+                    it -> it.isEnabled() && it.isLogToLogcat())
+                .map(IProtoLogGroup::name).collect(Collectors.joining(" "))
+                + "\nLogging definitions loaded: " + mViewerConfig.knownViewerStringsNumber();
+    }
+
+    /**
+     * Writes the log buffer to a new file for the bugreport.
+     *
+     * This method is synchronized with {@code #startProtoLog(PrintWriter)} and
+     * {@link #stopProtoLog(PrintWriter, boolean)}.
+     */
+    public void writeProtoLogToFile() {
+        synchronized (mProtoLogEnabledLock) {
+            writeProtoLogToFileLocked();
+        }
+    }
+
+    private void writeProtoLogToFileLocked() {
+        try {
+            long offset =
+                    (System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000));
+            ProtoOutputStream proto = new ProtoOutputStream();
+            proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
+            proto.write(VERSION, PROTOLOG_VERSION);
+            proto.write(REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS, offset);
+            mBuffer.writeTraceToFile(mLogFile, proto);
+        } catch (IOException e) {
+            Slog.e(TAG, "Unable to write buffer to file", e);
+        }
+    }
+
+    static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
+        Slog.i(TAG, msg);
+        if (pw != null) {
+            pw.println(msg);
+            pw.flush();
+        }
+    }
+}
+
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index 73d148c..9f7436a 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -36,7 +36,18 @@
             Consts.TAG_WM),
     WM_DEBUG_ADD_REMOVE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
             Consts.TAG_WM),
-    WM_DEBUG_FOCUS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
+    WM_DEBUG_CONFIGURATION(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM),
+    WM_DEBUG_SWITCH(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM),
+    WM_DEBUG_CONTAINERS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM),
+    WM_DEBUG_FOCUS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM),
+    WM_DEBUG_IMMERSIVE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM),
+    WM_DEBUG_LOCKTASK(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM),
     WM_DEBUG_STARTING_WINDOW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
             Consts.TAG_WM),
     WM_SHOW_TRANSACTIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
@@ -59,6 +70,8 @@
             Consts.TAG_WM),
     WM_DEBUG_IME(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
             Consts.TAG_WM),
+    WM_DEBUG_WINDOW_ORGANIZER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM),
     TEST_GROUP(true, true, false, "WindowManagetProtoLogTest");
 
     private final boolean mEnabled;
diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
index 6874f10..10224a4 100644
--- a/core/java/com/android/internal/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -16,58 +16,22 @@
 
 package com.android.internal.protolog;
 
-import static com.android.internal.protolog.ProtoLogFileProto.LOG;
-import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER;
-import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_H;
-import static com.android.internal.protolog.ProtoLogFileProto.MAGIC_NUMBER_L;
-import static com.android.internal.protolog.ProtoLogFileProto.REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS;
-import static com.android.internal.protolog.ProtoLogFileProto.VERSION;
-import static com.android.internal.protolog.ProtoLogMessage.BOOLEAN_PARAMS;
-import static com.android.internal.protolog.ProtoLogMessage.DOUBLE_PARAMS;
-import static com.android.internal.protolog.ProtoLogMessage.ELAPSED_REALTIME_NANOS;
-import static com.android.internal.protolog.ProtoLogMessage.MESSAGE_HASH;
-import static com.android.internal.protolog.ProtoLogMessage.SINT64_PARAMS;
-import static com.android.internal.protolog.ProtoLogMessage.STR_PARAMS;
-
 import android.annotation.Nullable;
-import android.os.ShellCommand;
-import android.os.SystemClock;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.IProtoLogGroup;
-import com.android.internal.protolog.common.LogDataType;
-import com.android.internal.util.TraceBuffer;
 
 import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.IllegalFormatConversionException;
-import java.util.TreeMap;
-import java.util.stream.Collectors;
-
 
 /**
  * A service for the ProtoLog logging system.
  */
-public class ProtoLogImpl {
-    private static final TreeMap<String, IProtoLogGroup> LOG_GROUPS = new TreeMap<>();
+public class ProtoLogImpl extends BaseProtoLogImpl {
+    private static final int BUFFER_CAPACITY = 1024 * 1024;
+    private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.pb";
+    private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz";
 
-    /**
-     * A runnable to update the cached output of {@link #isEnabled}.
-     *
-     * Must be invoked after every action that could change the result of {@link #isEnabled}, eg.
-     * starting / stopping proto log, or enabling / disabling log groups.
-     */
-    public static Runnable sCacheUpdater = () -> { };
-
-    private static void addLogGroupEnum(IProtoLogGroup[] config) {
-        for (IProtoLogGroup group : config) {
-            LOG_GROUPS.put(group.name(), group);
-        }
-    }
+    private static ProtoLogImpl sServiceInstance = null;
 
     static {
         addLogGroupEnum(ProtoLogGroup.values());
@@ -124,30 +88,13 @@
                 || (group.isLogToProto() && getSingleInstance().isProtoEnabled());
     }
 
-    private static final int BUFFER_CAPACITY = 1024 * 1024;
-    private static final String LOG_FILENAME = "/data/misc/wmtrace/wm_log.pb";
-    private static final String VIEWER_CONFIG_FILENAME = "/system/etc/protolog.conf.json.gz";
-    private static final String TAG = "ProtoLog";
-    private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
-    static final String PROTOLOG_VERSION = "1.0.0";
-
-    private final File mLogFile;
-    private final TraceBuffer mBuffer;
-    private final ProtoLogViewerConfigReader mViewerConfig;
-
-    private boolean mProtoLogEnabled;
-    private boolean mProtoLogEnabledLockFree;
-    private final Object mProtoLogEnabledLock = new Object();
-
-    private static ProtoLogImpl sServiceInstance = null;
-
     /**
      * Returns the single instance of the ProtoLogImpl singleton class.
      */
     public static synchronized ProtoLogImpl getSingleInstance() {
         if (sServiceInstance == null) {
-            sServiceInstance = new ProtoLogImpl(new File(LOG_FILENAME), BUFFER_CAPACITY,
-                    new ProtoLogViewerConfigReader());
+            sServiceInstance = new ProtoLogImpl(
+                    new File(LOG_FILENAME), BUFFER_CAPACITY, new ProtoLogViewerConfigReader());
         }
         return sServiceInstance;
     }
@@ -157,307 +104,9 @@
         sServiceInstance = instance;
     }
 
-    @VisibleForTesting
-    public enum LogLevel {
-        DEBUG, VERBOSE, INFO, WARN, ERROR, WTF
-    }
-
-    /**
-     * Main log method, do not call directly.
-     */
-    @VisibleForTesting
-    public void log(LogLevel level, IProtoLogGroup group, int messageHash, int paramsMask,
-            @Nullable String messageString, Object[] args) {
-        if (group.isLogToProto()) {
-            logToProto(messageHash, paramsMask, args);
-        }
-        if (group.isLogToLogcat()) {
-            logToLogcat(group.getTag(), level, messageHash, messageString, args);
-        }
-    }
-
-    private void logToLogcat(String tag, LogLevel level, int messageHash,
-            @Nullable String messageString, Object[] args) {
-        String message = null;
-        if (messageString == null) {
-            messageString = mViewerConfig.getViewerString(messageHash);
-        }
-        if (messageString != null) {
-            try {
-                message = String.format(messageString, args);
-            } catch (IllegalFormatConversionException ex) {
-                Slog.w(TAG, "Invalid ProtoLog format string.", ex);
-            }
-        }
-        if (message == null) {
-            StringBuilder builder = new StringBuilder("UNKNOWN MESSAGE (" + messageHash + ")");
-            for (Object o : args) {
-                builder.append(" ").append(o);
-            }
-            message = builder.toString();
-        }
-        passToLogcat(tag, level, message);
-    }
-
-    /**
-     * SLog wrapper.
-     */
-    @VisibleForTesting
-    public void passToLogcat(String tag, LogLevel level, String message) {
-        switch (level) {
-            case DEBUG:
-                Slog.d(tag, message);
-                break;
-            case VERBOSE:
-                Slog.v(tag, message);
-                break;
-            case INFO:
-                Slog.i(tag, message);
-                break;
-            case WARN:
-                Slog.w(tag, message);
-                break;
-            case ERROR:
-                Slog.e(tag, message);
-                break;
-            case WTF:
-                Slog.wtf(tag, message);
-                break;
-        }
-    }
-
-    private void logToProto(int messageHash, int paramsMask, Object[] args) {
-        if (!isProtoEnabled()) {
-            return;
-        }
-        try {
-            ProtoOutputStream os = new ProtoOutputStream();
-            long token = os.start(LOG);
-            os.write(MESSAGE_HASH, messageHash);
-            os.write(ELAPSED_REALTIME_NANOS, SystemClock.elapsedRealtimeNanos());
-
-            if (args != null) {
-                int argIndex = 0;
-                ArrayList<Long> longParams = new ArrayList<>();
-                ArrayList<Double> doubleParams = new ArrayList<>();
-                ArrayList<Boolean> booleanParams = new ArrayList<>();
-                for (Object o : args) {
-                    int type = LogDataType.bitmaskToLogDataType(paramsMask, argIndex);
-                    try {
-                        switch (type) {
-                            case LogDataType.STRING:
-                                os.write(STR_PARAMS, o.toString());
-                                break;
-                            case LogDataType.LONG:
-                                longParams.add(((Number) o).longValue());
-                                break;
-                            case LogDataType.DOUBLE:
-                                doubleParams.add(((Number) o).doubleValue());
-                                break;
-                            case LogDataType.BOOLEAN:
-                                booleanParams.add((boolean) o);
-                                break;
-                        }
-                    } catch (ClassCastException ex) {
-                        // Should not happen unless there is an error in the ProtoLogTool.
-                        os.write(STR_PARAMS, "(INVALID PARAMS_MASK) " + o.toString());
-                        Slog.e(TAG, "Invalid ProtoLog paramsMask", ex);
-                    }
-                    argIndex++;
-                }
-                if (longParams.size() > 0) {
-                    os.writePackedSInt64(SINT64_PARAMS,
-                            longParams.stream().mapToLong(i -> i).toArray());
-                }
-                if (doubleParams.size() > 0) {
-                    os.writePackedDouble(DOUBLE_PARAMS,
-                            doubleParams.stream().mapToDouble(i -> i).toArray());
-                }
-                if (booleanParams.size() > 0) {
-                    boolean[] arr = new boolean[booleanParams.size()];
-                    for (int i = 0; i < booleanParams.size(); i++) {
-                        arr[i] = booleanParams.get(i);
-                    }
-                    os.writePackedBool(BOOLEAN_PARAMS, arr);
-                }
-            }
-            os.end(token);
-            mBuffer.add(os);
-        } catch (Exception e) {
-            Slog.e(TAG, "Exception while logging to proto", e);
-        }
-    }
-
-    public ProtoLogImpl(File file, int bufferCapacity, ProtoLogViewerConfigReader viewerConfig) {
-        mLogFile = file;
-        mBuffer = new TraceBuffer(bufferCapacity);
-        mViewerConfig = viewerConfig;
-    }
-
-    /**
-     * Starts the logging a circular proto buffer.
-     *
-     * @param pw Print writer
-     */
-    public void startProtoLog(@Nullable PrintWriter pw) {
-        if (isProtoEnabled()) {
-            return;
-        }
-        synchronized (mProtoLogEnabledLock) {
-            logAndPrintln(pw, "Start logging to " + mLogFile + ".");
-            mBuffer.resetBuffer();
-            mProtoLogEnabled = true;
-            mProtoLogEnabledLockFree = true;
-        }
-        sCacheUpdater.run();
-    }
-
-    /**
-     * Stops logging to proto.
-     *
-     * @param pw          Print writer
-     * @param writeToFile If the current buffer should be written to disk or not
-     */
-    public void stopProtoLog(@Nullable PrintWriter pw, boolean writeToFile) {
-        if (!isProtoEnabled()) {
-            return;
-        }
-        synchronized (mProtoLogEnabledLock) {
-            logAndPrintln(pw, "Stop logging to " + mLogFile + ". Waiting for log to flush.");
-            mProtoLogEnabled = mProtoLogEnabledLockFree = false;
-            if (writeToFile) {
-                writeProtoLogToFileLocked();
-                logAndPrintln(pw, "Log written to " + mLogFile + ".");
-            }
-            if (mProtoLogEnabled) {
-                logAndPrintln(pw, "ERROR: logging was re-enabled while waiting for flush.");
-                throw new IllegalStateException("logging enabled while waiting for flush.");
-            }
-        }
-        sCacheUpdater.run();
-    }
-
-    /**
-     * Returns {@code true} iff logging to proto is enabled.
-     */
-    public boolean isProtoEnabled() {
-        return mProtoLogEnabledLockFree;
-    }
-
-    private int setLogging(ShellCommand shell, boolean setTextLogging, boolean value) {
-        String group;
-        while ((group = shell.getNextArg()) != null) {
-            IProtoLogGroup g = LOG_GROUPS.get(group);
-            if (g != null) {
-                if (setTextLogging) {
-                    g.setLogToLogcat(value);
-                } else {
-                    g.setLogToProto(value);
-                }
-            } else {
-                logAndPrintln(shell.getOutPrintWriter(), "No IProtoLogGroup named " + group);
-                return -1;
-            }
-        }
-        sCacheUpdater.run();
-        return 0;
-    }
-
-    private int unknownCommand(PrintWriter pw) {
-        pw.println("Unknown command");
-        pw.println("Window manager logging options:");
-        pw.println("  start: Start proto logging");
-        pw.println("  stop: Stop proto logging");
-        pw.println("  enable [group...]: Enable proto logging for given groups");
-        pw.println("  disable [group...]: Disable proto logging for given groups");
-        pw.println("  enable-text [group...]: Enable logcat logging for given groups");
-        pw.println("  disable-text [group...]: Disable logcat logging for given groups");
-        return -1;
-    }
-
-    /**
-     * Responds to a shell command.
-     */
-    public int onShellCommand(ShellCommand shell) {
-        PrintWriter pw = shell.getOutPrintWriter();
-        String cmd = shell.getNextArg();
-        if (cmd == null) {
-            return unknownCommand(pw);
-        }
-        switch (cmd) {
-            case "start":
-                startProtoLog(pw);
-                return 0;
-            case "stop":
-                stopProtoLog(pw, true);
-                return 0;
-            case "status":
-                logAndPrintln(pw, getStatus());
-                return 0;
-            case "enable":
-                return setLogging(shell, false, true);
-            case "enable-text":
-                mViewerConfig.loadViewerConfig(pw, VIEWER_CONFIG_FILENAME);
-                return setLogging(shell, true, true);
-            case "disable":
-                return setLogging(shell, false, false);
-            case "disable-text":
-                return setLogging(shell, true, false);
-            default:
-                return unknownCommand(pw);
-        }
-    }
-
-    /**
-     * Returns a human-readable ProtoLog status text.
-     */
-    public String getStatus() {
-        return "ProtoLog status: "
-                + ((isProtoEnabled()) ? "Enabled" : "Disabled")
-                + "\nEnabled log groups: \n  Proto: "
-                + LOG_GROUPS.values().stream().filter(
-                    it -> it.isEnabled() && it.isLogToProto())
-                .map(IProtoLogGroup::name).collect(Collectors.joining(" "))
-                + "\n  Logcat: "
-                + LOG_GROUPS.values().stream().filter(
-                    it -> it.isEnabled() && it.isLogToLogcat())
-                .map(IProtoLogGroup::name).collect(Collectors.joining(" "))
-                + "\nLogging definitions loaded: " + mViewerConfig.knownViewerStringsNumber();
-    }
-
-    /**
-     * Writes the log buffer to a new file for the bugreport.
-     *
-     * This method is synchronized with {@code #startProtoLog(PrintWriter)} and
-     * {@link #stopProtoLog(PrintWriter, boolean)}.
-     */
-    public void writeProtoLogToFile() {
-        synchronized (mProtoLogEnabledLock) {
-            writeProtoLogToFileLocked();
-        }
-    }
-
-    private void writeProtoLogToFileLocked() {
-        try {
-            long offset =
-                    (System.currentTimeMillis() - (SystemClock.elapsedRealtimeNanos() / 1000000));
-            ProtoOutputStream proto = new ProtoOutputStream();
-            proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE);
-            proto.write(VERSION, PROTOLOG_VERSION);
-            proto.write(REAL_TIME_TO_ELAPSED_TIME_OFFSET_MILLIS, offset);
-            mBuffer.writeTraceToFile(mLogFile, proto);
-        } catch (IOException e) {
-            Slog.e(TAG, "Unable to write buffer to file", e);
-        }
-    }
-
-
-    static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
-        Slog.i(TAG, msg);
-        if (pw != null) {
-            pw.println(msg);
-            pw.flush();
-        }
+    public ProtoLogImpl(File logFile, int bufferCapacity,
+            ProtoLogViewerConfigReader viewConfigReader) {
+        super(logFile, VIEWER_CONFIG_FILENAME, bufferCapacity, viewConfigReader);
     }
 }
 
diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
index e381d30..aa30a77 100644
--- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
+++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
@@ -16,6 +16,9 @@
 
 package com.android.internal.protolog;
 
+import android.annotation.Nullable;
+import android.util.Slog;
+
 import org.json.JSONException;
 import org.json.JSONObject;
 
@@ -23,6 +26,7 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.util.Iterator;
@@ -34,6 +38,7 @@
  * Handles loading and parsing of ProtoLog viewer configuration.
  */
 public class ProtoLogViewerConfigReader {
+    private static final String TAG = "ProtoLogViewerConfigReader";
     private Map<Integer, String> mLogMessageMap = null;
 
     /** Returns message format string for its hash or null if unavailable. */
@@ -49,48 +54,54 @@
      * Reads the specified viewer configuration file. Does nothing if the config is already loaded.
      */
     public synchronized void loadViewerConfig(PrintWriter pw, String viewerConfigFilename) {
+        try {
+            loadViewerConfig(new GZIPInputStream(new FileInputStream(viewerConfigFilename)));
+            logAndPrintln(pw, "Loaded " + mLogMessageMap.size()
+                    + " log definitions from " + viewerConfigFilename);
+        } catch (FileNotFoundException e) {
+            logAndPrintln(pw, "Unable to load log definitions: File "
+                    + viewerConfigFilename + " not found." + e);
+        } catch (IOException e) {
+            logAndPrintln(pw, "Unable to load log definitions: IOException while reading "
+                    + viewerConfigFilename + ". " + e);
+        } catch (JSONException e) {
+            logAndPrintln(pw, "Unable to load log definitions: JSON parsing exception while reading "
+                    + viewerConfigFilename + ". " + e);
+        }
+    }
+
+    /**
+     * Reads the specified viewer configuration input stream.
+     * Does nothing if the config is already loaded.
+     */
+    public synchronized void loadViewerConfig(InputStream viewerConfigInputStream)
+            throws IOException, JSONException {
         if (mLogMessageMap != null) {
             return;
         }
-        try {
-            InputStreamReader config = new InputStreamReader(
-                    new GZIPInputStream(new FileInputStream(viewerConfigFilename)));
-            BufferedReader reader = new BufferedReader(config);
-            StringBuilder builder = new StringBuilder();
-            String line;
-            while ((line = reader.readLine()) != null) {
-                builder.append(line).append('\n');
-            }
-            reader.close();
-            JSONObject json = new JSONObject(builder.toString());
-            JSONObject messages = json.getJSONObject("messages");
+        InputStreamReader config = new InputStreamReader(viewerConfigInputStream);
+        BufferedReader reader = new BufferedReader(config);
+        StringBuilder builder = new StringBuilder();
+        String line;
+        while ((line = reader.readLine()) != null) {
+            builder.append(line).append('\n');
+        }
+        reader.close();
+        JSONObject json = new JSONObject(builder.toString());
+        JSONObject messages = json.getJSONObject("messages");
 
-            mLogMessageMap = new TreeMap<>();
-            Iterator it = messages.keys();
-            while (it.hasNext()) {
-                String key = (String) it.next();
-                try {
-                    int hash = Integer.parseInt(key);
-                    JSONObject val = messages.getJSONObject(key);
-                    String msg = val.getString("message");
-                    mLogMessageMap.put(hash, msg);
-                } catch (NumberFormatException expected) {
-                    // Not a messageHash - skip it
-                }
+        mLogMessageMap = new TreeMap<>();
+        Iterator it = messages.keys();
+        while (it.hasNext()) {
+            String key = (String) it.next();
+            try {
+                int hash = Integer.parseInt(key);
+                JSONObject val = messages.getJSONObject(key);
+                String msg = val.getString("message");
+                mLogMessageMap.put(hash, msg);
+            } catch (NumberFormatException expected) {
+                // Not a messageHash - skip it
             }
-            ProtoLogImpl.logAndPrintln(pw, "Loaded " + mLogMessageMap.size()
-                    + " log definitions from " + viewerConfigFilename);
-        } catch (FileNotFoundException e) {
-            ProtoLogImpl.logAndPrintln(pw, "Unable to load log definitions: File "
-                    + viewerConfigFilename + " not found." + e);
-        } catch (IOException e) {
-            ProtoLogImpl.logAndPrintln(pw,
-                    "Unable to load log definitions: IOException while reading "
-                    + viewerConfigFilename + ". " + e);
-        } catch (JSONException e) {
-            ProtoLogImpl.logAndPrintln(pw,
-                    "Unable to load log definitions: JSON parsing exception while reading "
-                            + viewerConfigFilename + ". " + e);
         }
     }
 
@@ -103,4 +114,12 @@
         }
         return 0;
     }
+
+    static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
+        Slog.i(TAG, msg);
+        if (pw != null) {
+            pw.println(msg);
+            pw.flush();
+        }
+    }
 }
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index b2c5a99..d41d307 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -61,7 +61,7 @@
     void onRadioPowerStateChanged(in int state);
     void onCallAttributesChanged(in CallAttributes callAttributes);
     void onEmergencyNumberListChanged(in Map emergencyNumberList);
-    void onOutgoingEmergencyCall(in EmergencyNumber placedEmergencyNumber);
+    void onOutgoingEmergencyCall(in EmergencyNumber placedEmergencyNumber, int subscriptionId);
     void onOutgoingEmergencySms(in EmergencyNumber sentEmergencyNumber);
     void onCallDisconnectCauseChanged(in int disconnectCause, in int preciseDisconnectCause);
     void onImsCallDisconnectCauseChanged(in ImsReasonInfo imsReasonInfo);
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index d5d635d..654b461 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -49,7 +49,8 @@
             in ICheckCredentialProgressCallback progressCallback);
     VerifyCredentialResponse verifyCredential(in LockscreenCredential credential, int userId, int flags);
     VerifyCredentialResponse verifyTiedProfileChallenge(in LockscreenCredential credential, int userId, int flags);
-    VerifyCredentialResponse verifyGatekeeperPassword(in byte[] gatekeeperPassword, long challenge, int userId);
+    VerifyCredentialResponse verifyGatekeeperPasswordHandle(long gatekeeperPasswordHandle, long challenge, int userId);
+    void removeGatekeeperPasswordHandle(long gatekeeperPasswordHandle);
     boolean checkVoldPassword(int userId);
     int getCredentialType(int userId);
     byte[] getHashFactor(in LockscreenCredential currentCredential, int userId);
diff --git a/core/java/com/android/internal/widget/LocalImageResolver.java b/core/java/com/android/internal/widget/LocalImageResolver.java
index 2302de2..b4e108f 100644
--- a/core/java/com/android/internal/widget/LocalImageResolver.java
+++ b/core/java/com/android/internal/widget/LocalImageResolver.java
@@ -23,6 +23,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.util.Log;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -31,6 +32,7 @@
  * A class to extract Bitmaps from a MessagingStyle message.
  */
 public class LocalImageResolver {
+    private static final String TAG = LocalImageResolver.class.getSimpleName();
 
     private static final int MAX_SAFE_ICON_SIZE_PX = 480;
 
@@ -60,11 +62,18 @@
 
     private static BitmapFactory.Options getBoundsOptionsForImage(Uri uri, Context context)
             throws IOException {
-        InputStream input = context.getContentResolver().openInputStream(uri);
         BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
-        onlyBoundsOptions.inJustDecodeBounds = true;
-        BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
-        input.close();
+        try (InputStream input = context.getContentResolver().openInputStream(uri)) {
+            if (input == null) {
+                throw new IllegalArgumentException();
+            }
+            onlyBoundsOptions.inJustDecodeBounds = true;
+            BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
+        } catch (IllegalArgumentException iae) {
+            onlyBoundsOptions.outWidth = -1;
+            onlyBoundsOptions.outHeight = -1;
+            Log.e(TAG, "error loading image", iae);
+        }
         return onlyBoundsOptions;
     }
 
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index f7370d6..08a9f48 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -130,14 +130,15 @@
     public @interface CredentialType {}
 
     /**
-     * Flag provided to {@link #verifyCredential(LockscreenCredential, long, int, int)} . If set,
-     * the method will return the Gatekeeper Password in the {@link VerifyCredentialResponse}.
+     * Flag provided to {@link #verifyCredential(LockscreenCredential, int, int)} . If set, the
+     * method will return a handle to the Gatekeeper Password in the
+     * {@link VerifyCredentialResponse}.
      */
-    public static final int VERIFY_FLAG_RETURN_GK_PW = 1 << 0;
+    public static final int VERIFY_FLAG_REQUEST_GK_PW_HANDLE = 1 << 0;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, value = {
-            VERIFY_FLAG_RETURN_GK_PW
+            VERIFY_FLAG_REQUEST_GK_PW_HANDLE
     })
     public @interface VerifyFlag {}
 
@@ -409,16 +410,16 @@
     }
 
     /**
-     * With the Gatekeeper Password returned via {@link #verifyCredential(LockscreenCredential,
-     * int, int)}, request Gatekeeper to create a HardwareAuthToken wrapping the given
-     * challenge.
+     * With the Gatekeeper Password Handle returned via {@link #verifyCredential(
+     * LockscreenCredential, int, int)}, request Gatekeeper to create a HardwareAuthToken wrapping
+     * the given challenge.
      */
     @NonNull
-    public VerifyCredentialResponse verifyGatekeeperPassword(@NonNull byte[] gatekeeperPassword,
+    public VerifyCredentialResponse verifyGatekeeperPasswordHandle(long gatekeeperPasswordHandle,
             long challenge, int userId) {
         try {
-            final VerifyCredentialResponse response = getLockSettings().verifyGatekeeperPassword(
-                    gatekeeperPassword, challenge, userId);
+            final VerifyCredentialResponse response = getLockSettings()
+                    .verifyGatekeeperPasswordHandle(gatekeeperPasswordHandle, challenge, userId);
             if (response == null) {
                 return VerifyCredentialResponse.ERROR;
             }
@@ -429,6 +430,14 @@
         }
     }
 
+    public void removeGatekeeperPasswordHandle(long gatekeeperPasswordHandle) {
+        try {
+            getLockSettings().removeGatekeeperPasswordHandle(gatekeeperPasswordHandle);
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to remove gatekeeper password handle", e);
+        }
+    }
+
     /**
      * Check to see if a credential matches the saved one.
      *
@@ -671,7 +680,7 @@
      */
     public boolean setLockCredential(@NonNull LockscreenCredential newCredential,
             @NonNull LockscreenCredential savedCredential, int userHandle) {
-        if (!hasSecureLockScreen()) {
+        if (!hasSecureLockScreen() && newCredential.getType() != CREDENTIAL_TYPE_NONE) {
             throw new UnsupportedOperationException(
                     "This operation requires the lock screen feature.");
         }
@@ -766,7 +775,7 @@
 
     /** Update the encryption password if it is enabled **/
     private void updateEncryptionPassword(final int type, final byte[] password) {
-        if (!hasSecureLockScreen()) {
+        if (!hasSecureLockScreen() && password != null && password.length != 0) {
             throw new UnsupportedOperationException(
                     "This operation requires the lock screen feature.");
         }
@@ -1566,7 +1575,7 @@
      */
     public boolean setLockCredentialWithToken(@NonNull LockscreenCredential credential,
             long tokenHandle, byte[] token, int userHandle) {
-        if (!hasSecureLockScreen()) {
+        if (!hasSecureLockScreen() && credential.getType() != CREDENTIAL_TYPE_NONE) {
             throw new UnsupportedOperationException(
                     "This operation requires the lock screen feature.");
         }
diff --git a/core/java/com/android/internal/widget/VerifyCredentialResponse.java b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
index e09eb42..ab14634 100644
--- a/core/java/com/android/internal/widget/VerifyCredentialResponse.java
+++ b/core/java/com/android/internal/widget/VerifyCredentialResponse.java
@@ -49,7 +49,7 @@
     private final @ResponseCode int mResponseCode;
     private final int mTimeout;
     @Nullable private final byte[] mGatekeeperHAT;
-    @Nullable private final byte[] mGatekeeperPw;
+    private final long mGatekeeperPasswordHandle;
 
     public static final Parcelable.Creator<VerifyCredentialResponse> CREATOR
             = new Parcelable.Creator<VerifyCredentialResponse>() {
@@ -58,10 +58,10 @@
             final @ResponseCode int responseCode = source.readInt();
             final int timeout = source.readInt();
             final byte[] gatekeeperHAT = source.createByteArray();
-            final byte[] gatekeeperPassword = source.createByteArray();
+            long gatekeeperPasswordHandle = source.readLong();
 
             return new VerifyCredentialResponse(responseCode, timeout, gatekeeperHAT,
-                    gatekeeperPassword);
+                    gatekeeperPasswordHandle);
         }
 
         @Override
@@ -72,7 +72,7 @@
 
     public static class Builder {
         @Nullable private byte[] mGatekeeperHAT;
-        @Nullable private byte[] mGatekeeperPassword;
+        private long mGatekeeperPasswordHandle;
 
         /**
          * @param gatekeeperHAT Gatekeeper HardwareAuthToken, minted upon successful authentication.
@@ -82,8 +82,8 @@
             return this;
         }
 
-        public Builder setGatekeeperPassword(byte[] gatekeeperPassword) {
-            mGatekeeperPassword = gatekeeperPassword;
+        public Builder setGatekeeperPasswordHandle(long gatekeeperPasswordHandle) {
+            mGatekeeperPasswordHandle = gatekeeperPasswordHandle;
             return this;
         }
 
@@ -96,7 +96,7 @@
             return new VerifyCredentialResponse(RESPONSE_OK,
                     0 /* timeout */,
                     mGatekeeperHAT,
-                    mGatekeeperPassword);
+                    mGatekeeperPasswordHandle);
         }
     }
 
@@ -110,7 +110,7 @@
         return new VerifyCredentialResponse(RESPONSE_RETRY,
                 timeout,
                 null /* gatekeeperHAT */,
-                null /* gatekeeperPassword */);
+                0L /* gatekeeperPasswordHandle */);
     }
 
     /**
@@ -121,20 +121,20 @@
         return new VerifyCredentialResponse(RESPONSE_ERROR,
                 0 /* timeout */,
                 null /* gatekeeperHAT */,
-                null /* gatekeeperPassword */);
+                0L /* gatekeeperPasswordHandle */);
     }
 
     private VerifyCredentialResponse(@ResponseCode int responseCode, int timeout,
-            @Nullable byte[] gatekeeperHAT, @Nullable byte[] gatekeeperPassword) {
+            @Nullable byte[] gatekeeperHAT, long gatekeeperPasswordHandle) {
         mResponseCode = responseCode;
         mTimeout = timeout;
         mGatekeeperHAT = gatekeeperHAT;
-        mGatekeeperPw = gatekeeperPassword;
+        mGatekeeperPasswordHandle = gatekeeperPasswordHandle;
     }
 
     public VerifyCredentialResponse stripPayload() {
         return new VerifyCredentialResponse(mResponseCode, mTimeout,
-                null /* gatekeeperHAT */, null /* gatekeeperPassword */);
+                null /* gatekeeperHAT */, 0L /* gatekeeperPasswordHandle */);
     }
 
     @Override
@@ -142,7 +142,7 @@
         dest.writeInt(mResponseCode);
         dest.writeInt(mTimeout);
         dest.writeByteArray(mGatekeeperHAT);
-        dest.writeByteArray(mGatekeeperPw);
+        dest.writeLong(mGatekeeperPasswordHandle);
     }
 
     @Override
@@ -155,9 +155,12 @@
         return mGatekeeperHAT;
     }
 
-    @Nullable
-    public byte[] getGatekeeperPw() {
-        return mGatekeeperPw;
+    public long getGatekeeperPasswordHandle() {
+        return mGatekeeperPasswordHandle;
+    }
+
+    public boolean containsGatekeeperPasswordHandle() {
+        return mGatekeeperPasswordHandle != 0L;
     }
 
     public int getTimeout() {
@@ -176,7 +179,7 @@
     public String toString() {
         return "Response: " + mResponseCode
                 + ", GK HAT: " + (mGatekeeperHAT != null)
-                + ", GK PW: " + (mGatekeeperPw != null);
+                + ", GK PW: " + (mGatekeeperPasswordHandle != 0L);
     }
 
     public static VerifyCredentialResponse fromGateKeeperResponse(
diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp
index 9ad4cd9..859b40a 100644
--- a/core/jni/android_hardware_camera2_CameraMetadata.cpp
+++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp
@@ -501,6 +501,15 @@
                              "Failed to read from parcel (error code %d)", err);
         return;
     }
+
+    // Update vendor descriptor cache if necessary
+    auto vendorId = metadata->getVendorId();
+    if ((vendorId != CAMERA_METADATA_INVALID_VENDOR_ID) &&
+            !VendorTagDescriptorCache::isVendorCachePresent(vendorId)) {
+        ALOGW("%s: Tag vendor id missing or cache not initialized, trying to update!",
+                __FUNCTION__);
+        CameraMetadata_setupGlobalVendorTagDescriptor(env, thiz);
+    }
 }
 
 static void CameraMetadata_writeToParcel(JNIEnv *env, jclass thiz, jobject parcel, jlong ptr) {
@@ -642,9 +651,7 @@
     CameraMetadata* metadata = CameraMetadata_getPointerNoThrow(ptr);
     metadata_vendor_id_t vendorId = CAMERA_METADATA_INVALID_VENDOR_ID;
     if (metadata) {
-        const camera_metadata_t *metaBuffer = metadata->getAndLock();
-        vendorId = get_camera_metadata_vendor_id(metaBuffer);
-        metadata->unlock(metaBuffer);
+        vendorId = metadata->getVendorId();
     }
 
     int tagType = get_local_camera_metadata_tag_type_vendor_id(tag, vendorId);
@@ -673,9 +680,7 @@
     if (metadata) {
         sp<VendorTagDescriptorCache> cache = VendorTagDescriptorCache::getGlobalVendorTagCache();
         if (cache.get()) {
-            const camera_metadata_t *metaBuffer = metadata->getAndLock();
-            metadata_vendor_id_t vendorId = get_camera_metadata_vendor_id(metaBuffer);
-            metadata->unlock(metaBuffer);
+            auto vendorId = metadata->getVendorId();
             cache->getVendorTagDescriptor(vendorId, &vTags);
         }
     }
@@ -703,10 +708,8 @@
         CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, ptr);
         if (metadata == NULL) return NULL;
 
-        const camera_metadata_t *metaBuffer = metadata->getAndLock();
-        vendorId = get_camera_metadata_vendor_id(metaBuffer);
+        vendorId = metadata->getVendorId();
         cache->getVendorTagDescriptor(vendorId, &vTags);
-        metadata->unlock(metaBuffer);
         if (vTags.get() == nullptr) {
             return nullptr;
         }
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index ecdba3f..a063820 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -56,8 +56,7 @@
     jfieldID scaleFactor;
     jfieldID touchableRegion;
     jfieldID visible;
-    jfieldID canReceiveKeys;
-    jfieldID hasFocus;
+    jfieldID focusable;
     jfieldID hasWallpaper;
     jfieldID paused;
     jfieldID trustedOverlay;
@@ -145,10 +144,7 @@
 
     mInfo.visible = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.visible);
-    mInfo.canReceiveKeys = env->GetBooleanField(obj,
-            gInputWindowHandleClassInfo.canReceiveKeys);
-    mInfo.hasFocus = env->GetBooleanField(obj,
-            gInputWindowHandleClassInfo.hasFocus);
+    mInfo.focusable = env->GetBooleanField(obj, gInputWindowHandleClassInfo.focusable);
     mInfo.hasWallpaper = env->GetBooleanField(obj,
             gInputWindowHandleClassInfo.hasWallpaper);
     mInfo.paused = env->GetBooleanField(obj,
@@ -320,11 +316,7 @@
     GET_FIELD_ID(gInputWindowHandleClassInfo.visible, clazz,
             "visible", "Z");
 
-    GET_FIELD_ID(gInputWindowHandleClassInfo.canReceiveKeys, clazz,
-            "canReceiveKeys", "Z");
-
-    GET_FIELD_ID(gInputWindowHandleClassInfo.hasFocus, clazz,
-            "hasFocus", "Z");
+    GET_FIELD_ID(gInputWindowHandleClassInfo.focusable, clazz, "focusable", "Z");
 
     GET_FIELD_ID(gInputWindowHandleClassInfo.hasWallpaper, clazz,
             "hasWallpaper", "Z");
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index f8f841c..3af55fe 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -269,22 +269,9 @@
     return obj;
 }
 
-JHwRemoteBinder::JHwRemoteBinder(
-        JNIEnv *env, jobject thiz, const sp<hardware::IBinder> &binder)
-    : mBinder(binder) {
-    mDeathRecipientList = new HwBinderDeathRecipientList();
-    jclass clazz = env->GetObjectClass(thiz);
-    CHECK(clazz != NULL);
-
-    mObject = env->NewWeakGlobalRef(thiz);
-}
-
-JHwRemoteBinder::~JHwRemoteBinder() {
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
-
-    env->DeleteWeakGlobalRef(mObject);
-    mObject = NULL;
-}
+JHwRemoteBinder::JHwRemoteBinder(JNIEnv* env, jobject /* thiz */,
+                                 const sp<hardware::IBinder>& binder)
+      : mBinder(binder), mDeathRecipientList(new HwBinderDeathRecipientList()) {}
 
 sp<hardware::IBinder> JHwRemoteBinder::getBinder() const {
     return mBinder;
diff --git a/core/jni/android_os_HwRemoteBinder.h b/core/jni/android_os_HwRemoteBinder.h
index 4b5a4c8..7eb81f3 100644
--- a/core/jni/android_os_HwRemoteBinder.h
+++ b/core/jni/android_os_HwRemoteBinder.h
@@ -36,9 +36,13 @@
     std::vector<sp<HwBinderDeathRecipient>> mList;
     Mutex mLock;
 
+protected:
+    ~HwBinderDeathRecipientList() override;
+
 public:
-    HwBinderDeathRecipientList();
-    ~HwBinderDeathRecipientList();
+    explicit HwBinderDeathRecipientList();
+
+    DISALLOW_COPY_AND_ASSIGN(HwBinderDeathRecipientList);
 
     void add(const sp<HwBinderDeathRecipient>& recipient);
     void remove(const sp<HwBinderDeathRecipient>& recipient);
@@ -66,12 +70,7 @@
     void setBinder(const sp<hardware::IBinder> &binder);
     sp<HwBinderDeathRecipientList> getDeathRecipientList() const;
 
-protected:
-    virtual ~JHwRemoteBinder();
-
 private:
-    jobject mObject;
-
     sp<hardware::IBinder> mBinder;
     sp<HwBinderDeathRecipientList> mDeathRecipientList;
     DISALLOW_COPY_AND_ASSIGN(JHwRemoteBinder);
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 23af70a..cbcbe7f 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -613,7 +613,7 @@
     }
 
     // Do not change sched policy cgroup after boot complete.
-    rc = androidSetThreadPriority(pid, pri, !boot_completed);
+    rc = androidSetThreadPriorityAndPolicy(pid, pri, !boot_completed);
     if (rc != 0) {
         if (rc == INVALID_OPERATION) {
             signalExceptionForPriorityError(env, errno, pid);
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 979a69a..9ed71ac0 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -22,11 +22,12 @@
 
 #include <nativehelper/JNIHelp.h>
 
+#include <android-base/stringprintf.h>
 #include <android_runtime/AndroidRuntime.h>
+#include <input/InputTransport.h>
 #include <log/log.h>
 #include <utils/Looper.h>
-#include <utils/Vector.h>
-#include <input/InputTransport.h>
+#include <vector>
 #include "android_os_MessageQueue.h"
 #include "android_view_InputChannel.h"
 #include "android_view_KeyEvent.h"
@@ -52,6 +53,21 @@
     jmethodID onBatchedInputEventPending;
 } gInputEventReceiverClassInfo;
 
+// Add prefix to the beginning of each line in 'str'
+static std::string addPrefix(std::string str, std::string_view prefix) {
+    str.insert(0, prefix); // insert at the beginning of the first line
+    const size_t prefixLength = prefix.length();
+    size_t pos = prefixLength; // just inserted prefix. start at the end of it
+    while (true) {             // process all newline characters in 'str'
+        pos = str.find('\n', pos);
+        if (pos == std::string::npos) {
+            break;
+        }
+        str.insert(pos + 1, prefix); // insert prefix just after the '\n' character
+        pos += prefixLength + 1;     // advance the position past the newly inserted prefix
+    }
+    return str;
+}
 
 class NativeInputEventReceiver : public LooperCallback {
 public:
@@ -64,6 +80,7 @@
     status_t finishInputEvent(uint32_t seq, bool handled);
     status_t consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime,
             bool* outConsumedBatch);
+    std::string dump(const char* prefix);
 
 protected:
     virtual ~NativeInputEventReceiver();
@@ -80,7 +97,7 @@
     PreallocatedInputEventFactory mInputEventFactory;
     bool mBatchedInputEventPending;
     int mFdEvents;
-    Vector<Finish> mFinishQueue;
+    std::vector<Finish> mFinishQueue;
 
     void setFdEvents(int events);
 
@@ -128,7 +145,7 @@
     }
 
     status_t status = mInputConsumer.sendFinishedSignal(seq, handled);
-    if (status) {
+    if (status != OK) {
         if (status == WOULD_BLOCK) {
             if (kDebugDispatchCycle) {
                 ALOGD("channel '%s' ~ Could not send finished signal immediately.  "
@@ -137,7 +154,7 @@
             Finish finish;
             finish.seq = seq;
             finish.handled = handled;
-            mFinishQueue.add(finish);
+            mFinishQueue.push_back(finish);
             if (mFinishQueue.size() == 1) {
                 setFdEvents(ALOOPER_EVENT_INPUT | ALOOPER_EVENT_OUTPUT);
             }
@@ -162,6 +179,9 @@
 }
 
 int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
+    // Allowed return values of this function as documented in LooperCallback::handleEvent
+    constexpr int REMOVE_CALLBACK = 0;
+    constexpr int KEEP_CALLBACK = 1;
     if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
         // This error typically occurs when the publisher has closed the input channel
         // as part of removing a window or finishing an IME session, in which case
@@ -170,41 +190,42 @@
             ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred.  "
                     "events=0x%x", getInputChannelName().c_str(), events);
         }
-        return 0; // remove the callback
+        return REMOVE_CALLBACK;
     }
 
     if (events & ALOOPER_EVENT_INPUT) {
         JNIEnv* env = AndroidRuntime::getJNIEnv();
         status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
         mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
-        return status == OK || status == NO_MEMORY ? 1 : 0;
+        return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;
     }
 
     if (events & ALOOPER_EVENT_OUTPUT) {
         for (size_t i = 0; i < mFinishQueue.size(); i++) {
-            const Finish& finish = mFinishQueue.itemAt(i);
+            const Finish& finish = mFinishQueue[i];
             status_t status = mInputConsumer.sendFinishedSignal(finish.seq, finish.handled);
-            if (status) {
-                mFinishQueue.removeItemsAt(0, i);
+            if (status != OK) {
+                mFinishQueue.erase(mFinishQueue.begin(), mFinishQueue.begin() + i);
 
                 if (status == WOULD_BLOCK) {
                     if (kDebugDispatchCycle) {
                         ALOGD("channel '%s' ~ Sent %zu queued finish events; %zu left.",
-                                getInputChannelName().c_str(), i, mFinishQueue.size());
+                              getInputChannelName().c_str(), i, mFinishQueue.size());
                     }
-                    return 1; // keep the callback, try again later
+                    return KEEP_CALLBACK; // try again later
                 }
 
                 ALOGW("Failed to send finished signal on channel '%s'.  status=%d",
                         getInputChannelName().c_str(), status);
                 if (status != DEAD_OBJECT) {
                     JNIEnv* env = AndroidRuntime::getJNIEnv();
-                    String8 message;
-                    message.appendFormat("Failed to finish input event.  status=%d", status);
-                    jniThrowRuntimeException(env, message.string());
+                    std::string message =
+                            android::base::StringPrintf("Failed to finish input event.  status=%d",
+                                                        status);
+                    jniThrowRuntimeException(env, message.c_str());
                     mMessageQueue->raiseAndClearException(env, "finishInputEvent");
                 }
-                return 0; // remove the callback
+                return REMOVE_CALLBACK;
             }
         }
         if (kDebugDispatchCycle) {
@@ -213,12 +234,12 @@
         }
         mFinishQueue.clear();
         setFdEvents(ALOOPER_EVENT_INPUT);
-        return 1;
+        return KEEP_CALLBACK;
     }
 
     ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
             "events=0x%x", getInputChannelName().c_str(), events);
-    return 1;
+    return KEEP_CALLBACK;
 }
 
 status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
@@ -354,6 +375,23 @@
     }
 }
 
+std::string NativeInputEventReceiver::dump(const char* prefix) {
+    std::string out;
+    std::string consumerDump = addPrefix(mInputConsumer.dump(), "  ");
+    out = out + "mInputConsumer:\n" + consumerDump + "\n";
+
+    out += android::base::StringPrintf("mBatchedInputEventPending: %s\n",
+                                       toString(mBatchedInputEventPending));
+    out = out + "mFinishQueue:\n";
+    for (const Finish& finish : mFinishQueue) {
+        out += android::base::StringPrintf("  seq=%" PRIu32 " handled=%s\n", finish.seq,
+                                           toString(finish.handled));
+    }
+    if (mFinishQueue.empty()) {
+        out = out + "  <empty>\n";
+    }
+    return addPrefix(out, prefix);
+}
 
 static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
         jobject inputChannelObj, jobject messageQueueObj) {
@@ -374,9 +412,10 @@
             receiverWeak, inputChannel, messageQueue);
     status_t status = receiver->initialize();
     if (status) {
-        String8 message;
-        message.appendFormat("Failed to initialize input event receiver.  status=%d", status);
-        jniThrowRuntimeException(env, message.string());
+        std::string message =
+                android::base::StringPrintf("Failed to initialize input event receiver.  status=%d",
+                                            status);
+        jniThrowRuntimeException(env, message.c_str());
         return 0;
     }
 
@@ -397,9 +436,9 @@
             reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
     status_t status = receiver->finishInputEvent(seq, handled);
     if (status && status != DEAD_OBJECT) {
-        String8 message;
-        message.appendFormat("Failed to finish input event.  status=%d", status);
-        jniThrowRuntimeException(env, message.string());
+        std::string message =
+                android::base::StringPrintf("Failed to finish input event.  status=%d", status);
+        jniThrowRuntimeException(env, message.c_str());
     }
 }
 
@@ -411,26 +450,31 @@
     status_t status = receiver->consumeEvents(env, true /*consumeBatches*/, frameTimeNanos,
             &consumedBatch);
     if (status && status != DEAD_OBJECT && !env->ExceptionCheck()) {
-        String8 message;
-        message.appendFormat("Failed to consume batched input event.  status=%d", status);
-        jniThrowRuntimeException(env, message.string());
+        std::string message =
+                android::base::StringPrintf("Failed to consume batched input event.  status=%d",
+                                            status);
+        jniThrowRuntimeException(env, message.c_str());
         return JNI_FALSE;
     }
     return consumedBatch ? JNI_TRUE : JNI_FALSE;
 }
 
+static jstring nativeDump(JNIEnv* env, jclass clazz, jlong receiverPtr, jstring prefix) {
+    sp<NativeInputEventReceiver> receiver =
+            reinterpret_cast<NativeInputEventReceiver*>(receiverPtr);
+    ScopedUtfChars prefixChars(env, prefix);
+    return env->NewStringUTF(receiver->dump(prefixChars.c_str()).c_str());
+}
 
 static const JNINativeMethod gMethods[] = {
-    /* name, signature, funcPtr */
-    { "nativeInit",
-            "(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)J",
-            (void*)nativeInit },
-    { "nativeDispose", "(J)V",
-            (void*)nativeDispose },
-    { "nativeFinishInputEvent", "(JIZ)V",
-            (void*)nativeFinishInputEvent },
-    { "nativeConsumeBatchedInputEvents", "(JJ)Z",
-            (void*)nativeConsumeBatchedInputEvents },
+        /* name, signature, funcPtr */
+        {"nativeInit",
+         "(Ljava/lang/ref/WeakReference;Landroid/view/InputChannel;Landroid/os/MessageQueue;)J",
+         (void*)nativeInit},
+        {"nativeDispose", "(J)V", (void*)nativeDispose},
+        {"nativeFinishInputEvent", "(JIZ)V", (void*)nativeFinishInputEvent},
+        {"nativeConsumeBatchedInputEvents", "(JJ)Z", (void*)nativeConsumeBatchedInputEvents},
+        {"nativeDump", "(JLjava/lang/String;)Ljava/lang/String;", (void*)nativeDump},
 };
 
 int register_android_view_InputEventReceiver(JNIEnv* env) {
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 6eb8904..6212bcb 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2738,4 +2738,14 @@
     // CATEGORY: SETTINGS
     // OS: S
     SETTINGS_COLUMBUS = 1848;
+
+    // OPEN: Settings > Accessibility > Magnification > Settings > Magnification area > Magnification switch shortcut dialog
+    // CATEGORY: SETTINGS
+    // OS: S
+    DIALOG_MAGNIFICATION_SWITCH_SHORTCUT = 1849;
+
+    // OPEN: Settings > Network & internet > Adaptive connectivity
+    // CATEGORY: SETTINGS
+    // OS: R QPR
+    ADAPTIVE_CONNECTIVITY_CATEGORY = 1850;
 }
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index d5619ca..9fccdaf 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -507,7 +507,7 @@
     }
     optional IntentFirewall intent_firewall = 65;
 
-    optional SettingProto job_scheduler_constants = 66 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    reserved 66; // job_scheduler_constants
     optional SettingProto job_scheduler_quota_controller_constants = 149 [ (android.privacy).dest = DEST_AUTOMATIC ];
     reserved 150; // job_scheduler_time_controller_constants
 
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index 0455d58..a2f2c46 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -174,6 +174,16 @@
     optional BatterySaverStateMachineProto battery_saver_state_machine = 50;
     // Attentive timeout in ms. The timeout is disabled if it is set to -1.
     optional sint32 attentive_timeout_ms = 51;
+    // The time (in the elapsed realtime timebase) at which the battery level will reach 0%. This
+    // is provided as an enhanced estimate and only valid if
+    // last_enhanced_discharge_time_updated_elapsed is greater than 0.
+    optional int64 enhanced_discharge_time_elapsed = 52;
+    // Timestamp (in the elapsed realtime timebase) of last update to enhanced battery estimate
+    // data.
+    optional int64 last_enhanced_discharge_time_updated_elapsed = 53;
+    // Whether or not the current enhanced discharge prediction is personalized based on device
+    // usage or not.
+    optional bool is_enhanced_discharge_prediction_personalized = 54;
 }
 
 // A com.android.server.power.PowerManagerService.SuspendBlockerImpl object.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b16d4b2..cdcb24b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -102,6 +102,7 @@
     <protected-broadcast android:name="android.os.action.POWER_SAVE_WHITELIST_CHANGED" />
     <protected-broadcast android:name="android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED" />
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED_INTERNAL" />
+    <protected-broadcast android:name="android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED" />
 
     <!-- @deprecated This is rarely used and will be phased out soon. -->
     <protected-broadcast android:name="android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED" />
@@ -3093,7 +3094,8 @@
         android:protectionLevel="signature" />
 
     <!-- Allows an application to be the status bar.  Currently used only by SystemUI.apk
-    @hide -->
+        @hide
+        @SystemApi -->
     <permission android:name="android.permission.STATUS_BAR_SERVICE"
         android:protectionLevel="signature" />
 
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 26dac61..b6f6627 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -515,9 +515,9 @@
     <string name="permdesc_changeWifiMulticastState" product="tv" msgid="1336952358450652595">"‏للسماح للتطبيق بتلقّي الحِزم التي يتم إرسالها إلى جميع الأجهزة على شبكة Wi-Fi باستخدام عناوين بث متعدد، وليس باستخدام جهاز Android TV فقط. ويؤدي ذلك إلى استخدام قدر أكبر من الطاقة يفوق ما يتم استهلاكه في وضع البث غير المتعدد."</string>
     <string name="permdesc_changeWifiMulticastState" product="default" msgid="8296627590220222740">"‏للسماح للتطبيق بتلقي الحزم التي يتم إرسالها إلى جميع الأجهزة على شبكة Wi-Fi باستخدام عناوين بث متعدد، وليس باستخدام هاتفك فقط. ويؤدي ذلك إلى استخدام قدر أكبر من الطاقة يفوق وضع البث غير المتعدد."</string>
     <string name="permlab_bluetoothAdmin" msgid="6490373569441946064">"الدخول إلى إعدادات بلوتوث"</string>
-    <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"للسماح للتطبيق بتهيئة لوحة البلوتوث المحلي، واكتشاف أجهزة التحكم عن بعد والاقتران بها."</string>
+    <string name="permdesc_bluetoothAdmin" product="tablet" msgid="5370837055438574863">"للسماح للتطبيق بإعداد لوحة البلوتوث المحلي، واكتشاف أجهزة التحكم عن بعد والاقتران بها."</string>
     <string name="permdesc_bluetoothAdmin" product="tv" msgid="1623992984547014588">"‏للسماح للتطبيق بضبط البلوتوث على جهاز Android TV واكتشاف الأجهزة البعيدة والاقتران بها."</string>
-    <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"للسماح للتطبيق بتهيئة هاتف البلوتوث المحلي، واكتشاف أجهزة التحكم عن بعد والاقتران بها."</string>
+    <string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"للسماح للتطبيق بإعداد هاتف البلوتوث المحلي، واكتشاف أجهزة التحكم عن بعد والاقتران بها."</string>
     <string name="permlab_accessWimaxState" msgid="7029563339012437434">"‏الاتصال بـشبكة WiMAX وقطع الاتصال بها"</string>
     <string name="permdesc_accessWimaxState" msgid="5372734776802067708">"‏للسماح للتطبيق بتحديد ما إذا تم تفعيل WiMAX وتحديد معلومات حول أي شبكات WiMAX متصلة."</string>
     <string name="permlab_changeWimaxState" msgid="6223305780806267462">"‏تغيير حالة WiMAX"</string>
@@ -1412,7 +1412,7 @@
     <string name="select_input_method" msgid="3971267998568587025">"اختيار أسلوب الإدخال"</string>
     <string name="show_ime" msgid="6406112007347443383">"استمرار عرضها على الشاشة أثناء نشاط لوحة المفاتيح الفعلية"</string>
     <string name="hardware" msgid="1800597768237606953">"إظهار لوحة المفاتيح الافتراضية"</string>
-    <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"تهيئة لوحة المفاتيح الفعلية"</string>
+    <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"إعداد لوحة المفاتيح الفعلية"</string>
     <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"انقر لاختيار لغة وتنسيق"</string>
     <string name="fast_scroll_alphabet" msgid="8854435958703888376">" أ ب ت ث ج ح خ د ذ ر ز س ش ص ض ط ظ ع غ ف ق ك ل م ن ه و ي"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789 أ ب ت ث ج ح خ د ذ ر ز س ش ص ض ط ظ ع غ ف ق ك ل م ن ه و ي"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 085df65..f9e3e2f 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -945,7 +945,7 @@
     <string name="autofill_postal_code" msgid="7034789388968295591">"Kode pos"</string>
     <string name="autofill_state" msgid="3341725337190434069">"Negara Bagian"</string>
     <string name="autofill_zip_code" msgid="1315503730274962450">"Kode pos"</string>
-    <string name="autofill_county" msgid="7781382735643492173">"Wilayah"</string>
+    <string name="autofill_county" msgid="7781382735643492173">"County"</string>
     <string name="autofill_island" msgid="5367139008536593734">"Pulau"</string>
     <string name="autofill_district" msgid="6428712062213557327">"Distrik"</string>
     <string name="autofill_department" msgid="9047276226873531529">"Departemen"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 8871293..df7781e 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -1773,7 +1773,7 @@
     <string name="restr_pin_try_later" msgid="5897719962541636727">"Обиди се повторно подоцна"</string>
     <string name="immersive_cling_title" msgid="2307034298721541791">"Се прикажува на цел екран"</string>
     <string name="immersive_cling_description" msgid="7092737175345204832">"За да излезете, повлечете одозгора надолу."</string>
-    <string name="immersive_cling_positive" msgid="7047498036346489883">"Разбрав"</string>
+    <string name="immersive_cling_positive" msgid="7047498036346489883">"Сфатив"</string>
     <string name="done_label" msgid="7283767013231718521">"Готово"</string>
     <string name="hour_picker_description" msgid="5153757582093524635">"Приказ на часови во кружно движење"</string>
     <string name="minute_picker_description" msgid="9029797023621927294">"Приказ на минути во кружно движење"</string>
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 000e870..0a751dd 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -408,6 +408,9 @@
             int originalVirtualDisplayOrientation = virtualDisplayContext.getResources()
                     .getConfiguration().orientation;
 
+
+            // Perform global config change and verify there is no config change in derived display
+            // context.
             Configuration newAppConfig = new Configuration(originalAppConfig);
             newAppConfig.seq++;
             newAppConfig.orientation = newAppConfig.orientation == ORIENTATION_PORTRAIT
@@ -417,7 +420,7 @@
             activityThread.handleConfigurationChanged(newAppConfig);
 
             try {
-                assertEquals("Virtual display orientation should not change when process"
+                assertEquals("Virtual display orientation must not change when process"
                                 + " configuration orientation changes.",
                         originalVirtualDisplayOrientation,
                         virtualDisplayContext.getResources().getConfiguration().orientation);
@@ -438,6 +441,50 @@
     }
 
     @Test
+    public void testActivityOrientationChanged_DoesntOverrideVirtualDisplayOrientation() {
+        final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
+        final ActivityThread activityThread = activity.getActivityThread();
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            Configuration originalActivityConfig =
+                    new Configuration(activity.getResources().getConfiguration());
+            DisplayManager dm = activity.getSystemService(DisplayManager.class);
+
+            int virtualDisplayWidth;
+            int virtualDisplayHeight;
+            if (originalActivityConfig.orientation == ORIENTATION_PORTRAIT) {
+                virtualDisplayWidth = 100;
+                virtualDisplayHeight = 200;
+            } else {
+                virtualDisplayWidth = 200;
+                virtualDisplayHeight = 100;
+            }
+            Display virtualDisplay = dm.createVirtualDisplay("virtual-display",
+                    virtualDisplayWidth, virtualDisplayHeight, 200, null, 0).getDisplay();
+            Context virtualDisplayContext = activity.createDisplayContext(virtualDisplay);
+            int originalVirtualDisplayOrientation = virtualDisplayContext.getResources()
+                    .getConfiguration().orientation;
+
+            // Perform activity config change and verify there is no config change in derived
+            // display context.
+            Configuration newActivityConfig = new Configuration(originalActivityConfig);
+            newActivityConfig.seq++;
+            newActivityConfig.orientation = newActivityConfig.orientation == ORIENTATION_PORTRAIT
+                    ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+
+            activityThread.updatePendingActivityConfiguration(activity.getActivityToken(),
+                    newActivityConfig);
+            activityThread.handleActivityConfigurationChanged(activity.getActivityToken(),
+                    newActivityConfig, INVALID_DISPLAY);
+
+            assertEquals("Virtual display orientation must not change when activity"
+                            + " configuration orientation changes.",
+                    originalVirtualDisplayOrientation,
+                    virtualDisplayContext.getResources().getConfiguration().orientation);
+        });
+    }
+
+    @Test
     public void testHandleConfigurationChanged_DoesntOverrideActivityConfig() {
         final TestActivity activity = mActivityTestRule.launchActivity(new Intent());
 
diff --git a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java
index 72391f4..db127c6 100644
--- a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java
+++ b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneCapabilitiesTest.java
@@ -22,6 +22,7 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
 
 import org.junit.Test;
 
@@ -31,11 +32,22 @@
 
     @Test
     public void testEquals() {
-        TimeZoneCapabilities.Builder builder1 = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
+        TimeZoneConfiguration configuration1 = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        TimeZoneConfiguration configuration2 = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionEnabled(false)
+                .setGeoDetectionEnabled(false)
+                .build();
+
+        TimeZoneCapabilities.Builder builder1 = new TimeZoneCapabilities.Builder()
+                .setConfiguration(configuration1)
                 .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setSuggestManualTimeZone(CAPABILITY_POSSESSED);
-        TimeZoneCapabilities.Builder builder2 = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
+        TimeZoneCapabilities.Builder builder2 = new TimeZoneCapabilities.Builder()
+                .setConfiguration(configuration1)
                 .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setSuggestManualTimeZone(CAPABILITY_POSSESSED);
@@ -45,6 +57,20 @@
             assertEquals(one, two);
         }
 
+        builder2.setConfiguration(configuration2);
+        {
+            TimeZoneCapabilities one = builder1.build();
+            TimeZoneCapabilities two = builder2.build();
+            assertNotEquals(one, two);
+        }
+
+        builder1.setConfiguration(configuration2);
+        {
+            TimeZoneCapabilities one = builder1.build();
+            TimeZoneCapabilities two = builder2.build();
+            assertEquals(one, two);
+        }
+
         builder2.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED);
         {
             TimeZoneCapabilities one = builder1.build();
@@ -90,7 +116,12 @@
 
     @Test
     public void testParcelable() {
-        TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
+        TimeZoneConfiguration configuration = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder()
+                .setConfiguration(configuration)
                 .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
                 .setSuggestManualTimeZone(CAPABILITY_POSSESSED);
@@ -105,4 +136,51 @@
         builder.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED);
         assertRoundTripParcelable(builder.build());
     }
+
+    @Test
+    public void testApplyUpdate_permitted() {
+        TimeZoneConfiguration oldConfiguration =
+                new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                        .setAutoDetectionEnabled(true)
+                        .setGeoDetectionEnabled(true)
+                        .build();
+        TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder()
+                .setConfiguration(oldConfiguration)
+                .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
+                .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
+                .setSuggestManualTimeZone(CAPABILITY_POSSESSED)
+                .build();
+        assertEquals(oldConfiguration, capabilities.getConfiguration());
+
+        TimeZoneConfiguration configChange = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionEnabled(false)
+                .build();
+
+        TimeZoneConfiguration expected = new TimeZoneConfiguration.Builder(oldConfiguration)
+                .setAutoDetectionEnabled(false)
+                .build();
+        assertEquals(expected, capabilities.applyUpdate(configChange));
+    }
+
+    @Test
+    public void testApplyUpdate_notPermitted() {
+        TimeZoneConfiguration oldConfiguration =
+                new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                        .setAutoDetectionEnabled(true)
+                        .setGeoDetectionEnabled(true)
+                        .build();
+        TimeZoneCapabilities capabilities = new TimeZoneCapabilities.Builder()
+                .setConfiguration(oldConfiguration)
+                .setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED)
+                .setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED)
+                .setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED)
+                .build();
+        assertEquals(oldConfiguration, capabilities.getConfiguration());
+
+        TimeZoneConfiguration configChange = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionEnabled(false)
+                .build();
+
+        assertNull(capabilities.applyUpdate(configChange));
+    }
 }
diff --git a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java
index 00dc73e..faf908d 100644
--- a/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java
+++ b/core/tests/coretests/src/android/app/timezonedetector/TimeZoneConfigurationTest.java
@@ -27,11 +27,14 @@
 
 public class TimeZoneConfigurationTest {
 
+    private static final int ARBITRARY_USER_ID = 9876;
+
     @Test
     public void testBuilder_copyConstructor() {
-        TimeZoneConfiguration.Builder builder1 = new TimeZoneConfiguration.Builder()
-                .setAutoDetectionEnabled(true)
-                .setGeoDetectionEnabled(true);
+        TimeZoneConfiguration.Builder builder1 =
+                new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                        .setAutoDetectionEnabled(true)
+                        .setGeoDetectionEnabled(true);
         TimeZoneConfiguration configuration1 = builder1.build();
 
         TimeZoneConfiguration configuration2 =
@@ -41,28 +44,28 @@
     }
 
     @Test
-    public void testIsComplete() {
-        TimeZoneConfiguration.Builder builder =
-                new TimeZoneConfiguration.Builder();
-        assertFalse(builder.build().isComplete());
+    public void testIntrospectionMethods() {
+        TimeZoneConfiguration empty = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID).build();
+        assertFalse(empty.isComplete());
+        assertFalse(empty.hasSetting(TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED));
 
-        builder.setAutoDetectionEnabled(true);
-        assertFalse(builder.build().isComplete());
-
-        builder.setGeoDetectionEnabled(true);
-        assertTrue(builder.build().isComplete());
+        TimeZoneConfiguration completeConfig = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        assertTrue(completeConfig.isComplete());
+        assertTrue(completeConfig.hasSetting(TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED));
     }
 
     @Test
     public void testBuilder_mergeProperties() {
-        TimeZoneConfiguration configuration1 =
-                new TimeZoneConfiguration.Builder()
-                        .setAutoDetectionEnabled(true)
-                        .build();
+        TimeZoneConfiguration configuration1 = new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionEnabled(true)
+                .build();
 
         {
             TimeZoneConfiguration mergedEmptyAnd1 =
-                    new TimeZoneConfiguration.Builder()
+                    new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
                             .mergeProperties(configuration1)
                             .build();
             assertEquals(configuration1, mergedEmptyAnd1);
@@ -70,7 +73,7 @@
 
         {
             TimeZoneConfiguration configuration2 =
-                    new TimeZoneConfiguration.Builder()
+                    new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
                             .setAutoDetectionEnabled(false)
                             .build();
 
@@ -87,14 +90,22 @@
     @Test
     public void testEquals() {
         TimeZoneConfiguration.Builder builder1 =
-                new TimeZoneConfiguration.Builder();
+                new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID);
         {
             TimeZoneConfiguration one = builder1.build();
             assertEquals(one, one);
         }
 
+        {
+            TimeZoneConfiguration.Builder differentUserBuilder =
+                    new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID + 1);
+            TimeZoneConfiguration one = builder1.build();
+            TimeZoneConfiguration two = differentUserBuilder.build();
+            assertNotEquals(one, two);
+        }
+
         TimeZoneConfiguration.Builder builder2 =
-                new TimeZoneConfiguration.Builder();
+                new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID);
         {
             TimeZoneConfiguration one = builder1.build();
             TimeZoneConfiguration two = builder2.build();
@@ -148,7 +159,7 @@
     @Test
     public void testParcelable() {
         TimeZoneConfiguration.Builder builder =
-                new TimeZoneConfiguration.Builder();
+                new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID);
         assertRoundTripParcelable(builder.build());
 
         builder.setAutoDetectionEnabled(true);
diff --git a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
index efcd458..45adf83 100644
--- a/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
+++ b/core/tests/coretests/src/android/content/res/ResourcesManagerTest.java
@@ -29,31 +29,50 @@
 
 import junit.framework.TestCase;
 
+import java.util.HashMap;
+import java.util.Map;
+
 public class ResourcesManagerTest extends TestCase {
+    private static final int SECONDARY_DISPLAY_ID = 1;
     private static final String APP_ONE_RES_DIR = "app_one.apk";
     private static final String APP_ONE_RES_SPLIT_DIR = "app_one_split.apk";
     private static final String APP_TWO_RES_DIR = "app_two.apk";
     private static final String LIB_RES_DIR = "lib.apk";
 
     private ResourcesManager mResourcesManager;
-    private DisplayMetrics mDisplayMetrics;
+    private Map<Integer, DisplayMetrics> mDisplayMetricsMap;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
-        mDisplayMetrics = new DisplayMetrics();
-        mDisplayMetrics.setToDefaults();
+        mDisplayMetricsMap = new HashMap<>();
+
+        DisplayMetrics defaultDisplayMetrics = new DisplayMetrics();
+        defaultDisplayMetrics.setToDefaults();
 
         // Override defaults (which take device specific properties).
-        mDisplayMetrics.density = 1.0f;
-        mDisplayMetrics.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
-        mDisplayMetrics.xdpi = DisplayMetrics.DENSITY_DEFAULT;
-        mDisplayMetrics.ydpi = DisplayMetrics.DENSITY_DEFAULT;
-        mDisplayMetrics.noncompatDensity = mDisplayMetrics.density;
-        mDisplayMetrics.noncompatDensityDpi = mDisplayMetrics.densityDpi;
-        mDisplayMetrics.noncompatXdpi = DisplayMetrics.DENSITY_DEFAULT;
-        mDisplayMetrics.noncompatYdpi = DisplayMetrics.DENSITY_DEFAULT;
+        defaultDisplayMetrics.density = 1.0f;
+        defaultDisplayMetrics.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
+        defaultDisplayMetrics.xdpi = DisplayMetrics.DENSITY_DEFAULT;
+        defaultDisplayMetrics.ydpi = DisplayMetrics.DENSITY_DEFAULT;
+        defaultDisplayMetrics.widthPixels = 1440;
+        defaultDisplayMetrics.heightPixels = 2960;
+        defaultDisplayMetrics.noncompatDensity = defaultDisplayMetrics.density;
+        defaultDisplayMetrics.noncompatDensityDpi = defaultDisplayMetrics.densityDpi;
+        defaultDisplayMetrics.noncompatXdpi = DisplayMetrics.DENSITY_DEFAULT;
+        defaultDisplayMetrics.noncompatYdpi = DisplayMetrics.DENSITY_DEFAULT;
+        defaultDisplayMetrics.noncompatWidthPixels = defaultDisplayMetrics.widthPixels;
+        defaultDisplayMetrics.noncompatHeightPixels = defaultDisplayMetrics.heightPixels;
+        mDisplayMetricsMap.put(Display.DEFAULT_DISPLAY, defaultDisplayMetrics);
+
+        DisplayMetrics secondaryDisplayMetrics = new DisplayMetrics();
+        secondaryDisplayMetrics.setTo(defaultDisplayMetrics);
+        secondaryDisplayMetrics.widthPixels = 50;
+        secondaryDisplayMetrics.heightPixels = 100;
+        secondaryDisplayMetrics.noncompatWidthPixels = secondaryDisplayMetrics.widthPixels;
+        secondaryDisplayMetrics.noncompatHeightPixels = secondaryDisplayMetrics.heightPixels;
+        mDisplayMetricsMap.put(SECONDARY_DISPLAY_ID, secondaryDisplayMetrics);
 
         mResourcesManager = new ResourcesManager() {
             @Override
@@ -63,7 +82,7 @@
 
             @Override
             protected DisplayMetrics getDisplayMetrics(int displayId, DisplayAdjustments daj) {
-                return mDisplayMetrics;
+                return mDisplayMetricsMap.get(displayId);
             }
         };
     }
@@ -71,12 +90,12 @@
     @SmallTest
     public void testMultipleCallsWithIdenticalParametersCacheReference() {
         Resources resources = mResourcesManager.getResources(
-                null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+                null, APP_ONE_RES_DIR, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources);
 
         Resources newResources = mResourcesManager.getResources(
-                null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+                null, APP_ONE_RES_DIR, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(newResources);
         assertSame(resources, newResources);
@@ -85,14 +104,14 @@
     @SmallTest
     public void testMultipleCallsWithDifferentParametersReturnDifferentReferences() {
         Resources resources = mResourcesManager.getResources(
-                null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+                null, APP_ONE_RES_DIR, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources);
 
         Configuration overrideConfig = new Configuration();
         overrideConfig.smallestScreenWidthDp = 200;
         Resources newResources = mResourcesManager.getResources(
-                null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, overrideConfig,
+                null, APP_ONE_RES_DIR, null, null, null, null, overrideConfig,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(newResources);
         assertNotSame(resources, newResources);
@@ -101,13 +120,13 @@
     @SmallTest
     public void testAddingASplitCreatesANewImpl() {
         Resources resources1 = mResourcesManager.getResources(
-                null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+                null, APP_ONE_RES_DIR, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources1);
 
         Resources resources2 = mResourcesManager.getResources(
                 null, APP_ONE_RES_DIR, new String[] { APP_ONE_RES_SPLIT_DIR }, null, null,
-                Display.DEFAULT_DISPLAY, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO,null,
+                null, null, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null,
                 null);
         assertNotNull(resources2);
 
@@ -118,12 +137,12 @@
     @SmallTest
     public void testUpdateConfigurationUpdatesAllAssetManagers() {
         Resources resources1 = mResourcesManager.getResources(
-                null, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+                null, APP_ONE_RES_DIR, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources1);
 
         Resources resources2 = mResourcesManager.getResources(
-                null, APP_TWO_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+                null, APP_TWO_RES_DIR, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources2);
 
@@ -131,7 +150,7 @@
         final Configuration overrideConfig = new Configuration();
         overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
         Resources resources3 = mResourcesManager.getResources(
-                activity, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY,
+                activity, APP_ONE_RES_DIR, null, null, null, null,
                 overrideConfig, CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources3);
 
@@ -152,7 +171,7 @@
         final Configuration expectedConfig = new Configuration();
         expectedConfig.setToDefaults();
         expectedConfig.setLocales(LocaleList.getAdjustedDefault());
-        expectedConfig.densityDpi = mDisplayMetrics.densityDpi;
+        expectedConfig.densityDpi = mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).densityDpi;
         expectedConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
 
         assertEquals(expectedConfig, resources1.getConfiguration());
@@ -164,13 +183,13 @@
     public void testTwoActivitiesWithIdenticalParametersShareImpl() {
         Binder activity1 = new Binder();
         Resources resources1 = mResourcesManager.getResources(
-                activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+                activity1, APP_ONE_RES_DIR, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources1);
 
         Binder activity2 = new Binder();
         Resources resources2 = mResourcesManager.getResources(
-                activity2, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+                activity2, APP_ONE_RES_DIR, null, null, null, null, null,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources1);
 
@@ -201,7 +220,7 @@
         final Configuration overrideConfig = new Configuration();
         overrideConfig.orientation = Configuration.ORIENTATION_LANDSCAPE;
         mResourcesManager.updateResourcesForActivity(activity1, overrideConfig,
-                Display.DEFAULT_DISPLAY, false /* movedToDifferentDisplay */);
+                Display.DEFAULT_DISPLAY);
         assertSame(resources1, theme.getResources());
 
         // Make sure we can still access the data.
@@ -226,7 +245,7 @@
         Configuration config2 = new Configuration();
         config2.screenLayout |= Configuration.SCREENLAYOUT_ROUND_YES;
         Resources resources2 = mResourcesManager.getResources(
-                activity1, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, config2,
+                activity1, APP_ONE_RES_DIR, null, null, null, null, config2,
                 CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
         assertNotNull(resources2);
 
@@ -250,8 +269,7 @@
 
         // Now update the Activity base override, and both resources should update.
         config1.orientation = Configuration.ORIENTATION_LANDSCAPE;
-        mResourcesManager.updateResourcesForActivity(activity1, config1, Display.DEFAULT_DISPLAY,
-                false /* movedToDifferentDisplay */);
+        mResourcesManager.updateResourcesForActivity(activity1, config1, Display.DEFAULT_DISPLAY);
 
         expectedConfig1.orientation = Configuration.ORIENTATION_LANDSCAPE;
         assertEquals(expectedConfig1, resources1.getConfiguration());
@@ -290,4 +308,41 @@
         assertEquals(originalOverrideDensity,
                 resources.getDisplayAdjustments().getConfiguration().densityDpi);
     }
+
+    @SmallTest
+    public void testChangingActivityDisplayDoesntOverrideDisplayRequestedByResources() {
+        Binder activity = new Binder();
+
+        // Create a base token resources that are based on the default display.
+        Resources activityResources = mResourcesManager.createBaseTokenResources(
+                activity, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+                CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
+        // Create another resources that explicitly override the display of the base token above
+        // and set it to DEFAULT_DISPLAY.
+        Resources defaultDisplayResources = mResourcesManager.getResources(
+                activity, APP_ONE_RES_DIR, null, null, null, Display.DEFAULT_DISPLAY, null,
+                CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null, null);
+
+        assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels,
+                activityResources.getDisplayMetrics().widthPixels);
+        assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).heightPixels,
+                activityResources.getDisplayMetrics().heightPixels);
+        assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels,
+                defaultDisplayResources.getDisplayMetrics().widthPixels);
+        assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels,
+                defaultDisplayResources.getDisplayMetrics().widthPixels);
+
+        // Now change the display of the activity and ensure the activity's display metrics match
+        // the new display, but the other resources remain based on the default display.
+        mResourcesManager.updateResourcesForActivity(activity, null, SECONDARY_DISPLAY_ID);
+
+        assertEquals(mDisplayMetricsMap.get(SECONDARY_DISPLAY_ID).widthPixels,
+                activityResources.getDisplayMetrics().widthPixels);
+        assertEquals(mDisplayMetricsMap.get(SECONDARY_DISPLAY_ID).heightPixels,
+                activityResources.getDisplayMetrics().heightPixels);
+        assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels,
+                defaultDisplayResources.getDisplayMetrics().widthPixels);
+        assertEquals(mDisplayMetricsMap.get(Display.DEFAULT_DISPLAY).widthPixels,
+                defaultDisplayResources.getDisplayMetrics().widthPixels);
+    }
 }
diff --git a/core/tests/coretests/src/android/text/OWNERS b/core/tests/coretests/src/android/text/OWNERS
index a35c604..0b51b2d 100644
--- a/core/tests/coretests/src/android/text/OWNERS
+++ b/core/tests/coretests/src/android/text/OWNERS
@@ -1,5 +1,4 @@
 set noparent
 
 siyamed@google.com
-nona@google.com
-clarabayarri@google.com
\ No newline at end of file
+nona@google.com
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 7807f01..bef27e2 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -39,6 +39,7 @@
         BatteryStatsTimerTest.class,
         BatteryStatsUidTest.class,
         BatteryStatsUserLifecycleTests.class,
+        BstatsCpuTimesValidationTest.class,
         KernelCpuProcStringReaderTest.class,
         KernelCpuUidActiveTimeReaderTest.class,
         KernelCpuUidBpfMapReaderTest.class,
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
index c577eef..fe7c944 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
@@ -29,6 +29,8 @@
 
 LOCAL_DEX_PREOPT := false
 
+LOCAL_EMMA_INSTRUMENT := false
+
 mainDexList:= \
 	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
 
@@ -60,6 +62,8 @@
 
 LOCAL_DEX_PREOPT := false
 
+LOCAL_EMMA_INSTRUMENT := false
+
 mainDexList2:= \
 	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
 
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
index da40940..3636c73 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
@@ -33,6 +33,8 @@
 
 LOCAL_DEX_PREOPT := false
 
+LOCAL_EMMA_INSTRUMENT := false
+
 include $(BUILD_PACKAGE)
 
 $(mainDexList): $(full_classes_pre_proguard_jar) $(MAINDEXCLASSES) $(PROGUARD_DEPS)
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
index 665e22d..67f1fa5 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
@@ -28,6 +28,8 @@
 
 LOCAL_DEX_PREOPT := false
 
+LOCAL_EMMA_INSTRUMENT := false
+
 mainDexList:= \
 	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
 
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
index c827fa8..33871e5 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
@@ -28,6 +28,8 @@
 
 LOCAL_DEX_PREOPT := false
 
+LOCAL_EMMA_INSTRUMENT := false
+
 mainDexList:= \
 	$(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
 
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
index 3d6ad7d..1b267ee 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
@@ -31,6 +31,8 @@
 
 LOCAL_DEX_PREOPT := false
 
+LOCAL_EMMA_INSTRUMENT := false
+
 LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
 
 include $(BUILD_PACKAGE)
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 73296987..75eb7b6 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -13,6 +13,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-2121056984": {
+      "message": "%s",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "-2109936758": {
       "message": "removeAppToken make exiting: %s",
       "level": "VERBOSE",
@@ -37,12 +43,24 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-2049725903": {
+      "message": "Task back pressed on root taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "-2039580386": {
       "message": "Attempted to add input method window with unknown token %s.  Aborting.",
       "level": "WARN",
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-2029985709": {
+      "message": "setFocusedTask: taskId=%d",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-2024464438": {
       "message": "app-onAnimationFinished(): mOuter=%s",
       "level": "DEBUG",
@@ -73,6 +91,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
     },
+    "-1980468143": {
+      "message": "DisplayArea appeared name=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+    },
     "-1976930686": {
       "message": "Attempted to add Accessibility overlay window with bad token %s.  Aborting.",
       "level": "WARN",
@@ -91,6 +115,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1939861963": {
+      "message": "Create root task displayId=%d winMode=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "-1939358269": {
       "message": "mRecentScreenshotAnimator finish",
       "level": "DEBUG",
@@ -115,6 +145,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1895337367": {
+      "message": "Delete root task display=%d winMode=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "-1884933373": {
       "message": "enableScreenAfterBoot: mDisplayEnabled=%b mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. %s",
       "level": "INFO",
@@ -139,6 +175,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayRotation.java"
     },
+    "-1868048288": {
+      "message": "Updating to new configuration after starting activity.",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityStarter.java"
+    },
     "-1862269827": {
       "message": "applyAnimation: anim=%s transit=%s isEntrance=%b Callers=%s",
       "level": "VERBOSE",
@@ -163,6 +205,24 @@
       "group": "WM_DEBUG_RESIZE",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "-1810446914": {
+      "message": "Trying to update display configuration for system\/invalid process.",
+      "level": "WARN",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
+    "-1792633344": {
+      "message": "Register task organizer=%s uid=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
+    "-1791031393": {
+      "message": "Ensuring correct configuration: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-1782453012": {
       "message": "Checking theme of starting window: 0x%x",
       "level": "VERBOSE",
@@ -211,6 +271,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
+    "-1699018375": {
+      "message": "Adding activity %s to task %s callers: %s",
+      "level": "INFO",
+      "group": "WM_DEBUG_ADD_REMOVE",
+      "at": "com\/android\/server\/wm\/Task.java"
+    },
     "-1698815688": {
       "message": "Resetting app token %s of replacing window marks.",
       "level": "DEBUG",
@@ -229,12 +295,30 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1638958146": {
+      "message": "Removing activity %s from task=%s adding to task=%s Callers=%s",
+      "level": "INFO",
+      "group": "WM_DEBUG_ADD_REMOVE",
+      "at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java"
+    },
     "-1632122349": {
       "message": "Changing surface while display frozen: %s",
       "level": "VERBOSE",
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1630752478": {
+      "message": "removeLockedTask: removed %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
+    "-1598452494": {
+      "message": "activityDestroyedLocked: r=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_CONTAINERS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-1596995693": {
       "message": "startAnimation",
       "level": "DEBUG",
@@ -307,6 +391,18 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
+    "-1495062622": {
+      "message": "Can't report activity moved to display - client not running, activityRecord=%s, displayId=%d",
+      "level": "WARN",
+      "group": "WM_DEBUG_SWITCH",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
+    "-1492881555": {
+      "message": "Starting activity when config will change = %b",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityStarter.java"
+    },
     "-1471946192": {
       "message": "Marking app token %s with replacing child windows.",
       "level": "DEBUG",
@@ -355,6 +451,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-1364754753": {
+      "message": "Task vanished taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "-1352076759": {
       "message": "Removing app token: %s",
       "level": "VERBOSE",
@@ -379,6 +481,12 @@
       "group": "WM_DEBUG_IME",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "-1305755880": {
+      "message": "Initial config: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-1292329638": {
       "message": "Added starting %s: startingWindow=%s startingView=%s",
       "level": "VERBOSE",
@@ -445,6 +553,12 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimation.java"
     },
+    "-1155279885": {
+      "message": "Frontmost changed immersion: %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_IMMERSIVE",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-1144293044": {
       "message": "SURFACE SET FREEZE LAYER: %s",
       "level": "INFO",
@@ -475,6 +589,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayRotation.java"
     },
+    "-1115019498": {
+      "message": "Configuration & display unchanged in %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-1113134997": {
       "message": "Attempted to add application window with unknown token %s.  Aborting.",
       "level": "WARN",
@@ -559,12 +679,36 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "-951939129": {
+      "message": "Unregister task organizer=%s uid=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
+    "-930893991": {
+      "message": "Set sync ready, syncId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
+    },
+    "-929676529": {
+      "message": "Configuration changes for %s, allChanges=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-928291778": {
       "message": "applyAnimation: anim=%s nextAppTransition=%d transit=%s Callers=%s",
       "level": "VERBOSE",
       "group": "WM_DEBUG_APP_TRANSITIONS_ANIM",
       "at": "com\/android\/server\/wm\/AppTransition.java"
     },
+    "-927199900": {
+      "message": "Updating global configuration to: %s",
+      "level": "INFO",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-916108501": {
       "message": "Adding %s to %s",
       "level": "VERBOSE",
@@ -619,12 +763,24 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-846078709": {
+      "message": "Configuration doesn't matter in finishing %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-809771899": {
       "message": "findFocusedWindow: Reached focused app=%s",
       "level": "VERBOSE",
       "group": "WM_DEBUG_FOCUS_LIGHT",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
+    "-804217032": {
+      "message": "Skipping config check (will change): %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-793346159": {
       "message": "New transit into wallpaper: %s",
       "level": "VERBOSE",
@@ -673,11 +829,17 @@
       "group": "WM_DEBUG_SCREEN_ON",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-714291355": {
-      "message": "Losing delayed focus: %s",
-      "level": "INFO",
-      "group": "WM_DEBUG_FOCUS_LIGHT",
-      "at": "com\/android\/server\/wm\/WindowManagerService.java"
+    "-743431900": {
+      "message": "Configuration no differences in %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
+    "-716565534": {
+      "message": "moveActivityStackToFront: unfocusable activity=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
     "-694710814": {
       "message": "Pausing rotation during drag",
@@ -739,6 +901,12 @@
       "group": "WM_DEBUG_FOCUS_LIGHT",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
+    "-593535526": {
+      "message": "Binding proc %s with config %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/am\/ActivityManagerService.java"
+    },
     "-583031528": {
       "message": "%s",
       "level": "INFO",
@@ -757,6 +925,12 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-548282316": {
+      "message": "setLockTaskMode: Locking to %s Callers=%s",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "-547111355": {
       "message": "hideIme Control target: %s ",
       "level": "DEBUG",
@@ -781,6 +955,18 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-503656156": {
+      "message": "Update process config of %s to new config %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
+    "-497620140": {
+      "message": "Transaction ready, syncId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
+    },
     "-496681057": {
       "message": "Attempted to get remove mode of a display that does not exist: %d",
       "level": "WARN",
@@ -799,6 +985,12 @@
       "group": "WM_SHOW_SURFACE_ALLOC",
       "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
     },
+    "-449118559": {
+      "message": "Trying to update display configuration for invalid process, pid=%d",
+      "level": "WARN",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-445944810": {
       "message": "finish(%b): mCanceled=%b",
       "level": "DEBUG",
@@ -835,6 +1027,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/TaskSnapshotSurface.java"
     },
+    "-401282500": {
+      "message": "destroyIfPossible: r=%s destroy returned removed=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_CONTAINERS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-395922585": {
       "message": "InsetsSource setWin %s",
       "level": "DEBUG",
@@ -907,18 +1105,42 @@
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
+    "-317194205": {
+      "message": "clearLockedTasks: %s",
+      "level": "INFO",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "-303497363": {
       "message": "reparent: moving activity=%s to task=%d at %d",
       "level": "INFO",
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-272719931": {
+      "message": "startLockTaskModeLocked: %s",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
+    "-260960989": {
+      "message": "Removing and adding activity %s to stack at top callers=%s",
+      "level": "INFO",
+      "group": "WM_DEBUG_ADD_REMOVE",
+      "at": "com\/android\/server\/wm\/Task.java"
+    },
     "-251259736": {
       "message": "No longer freezing: %s",
       "level": "VERBOSE",
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-235225312": {
+      "message": "Skipping config check for initializing activity: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-198463978": {
       "message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b",
       "level": "VERBOSE",
@@ -943,6 +1165,12 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimationController.java"
     },
+    "-168799453": {
+      "message": "Allowing features %d:0x%s",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "-167822951": {
       "message": "Attempted to add starting window to token with already existing starting window",
       "level": "WARN",
@@ -979,6 +1207,12 @@
       "group": "WM_DEBUG_FOCUS_LIGHT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-90559682": {
+      "message": "Config is skipping already pausing %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "-87705714": {
       "message": "findFocusedWindow: focusedApp=null using new focus @ %s",
       "level": "VERBOSE",
@@ -1159,6 +1393,12 @@
       "group": "WM_SHOW_SURFACE_ALLOC",
       "at": "com\/android\/server\/wm\/BlackFrame.java"
     },
+    "174572959": {
+      "message": "DisplayArea info changed name=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+    },
     "184362060": {
       "message": "screenshotTask(%d): mCanceled=%b",
       "level": "DEBUG",
@@ -1201,6 +1441,12 @@
       "group": "WM_DEBUG_KEEP_SCREEN_ON",
       "at": "com\/android\/server\/wm\/RootWindowContainer.java"
     },
+    "232317536": {
+      "message": "Set intercept back pressed on root=%b",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "241961619": {
       "message": "Adding %s to %s",
       "level": "VERBOSE",
@@ -1219,6 +1465,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RemoteAnimationController.java"
     },
+    "251812577": {
+      "message": "Register display organizer=%s uid=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+    },
     "254883724": {
       "message": "addWindowToken: Attempted to add binder token: %s for already created window token: %s displayId=%d",
       "level": "WARN",
@@ -1267,6 +1519,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayRotation.java"
     },
+    "302969511": {
+      "message": "Task info changed taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "302992539": {
       "message": "addAnimation(%s)",
       "level": "DEBUG",
@@ -1303,6 +1561,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "355940361": {
+      "message": "Config is destroying non-running %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "371641947": {
       "message": "Window Manager Crash %s",
       "level": "WTF",
@@ -1315,18 +1579,18 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "374506950": {
+      "message": "Reporting activity moved to display, activityRecord=%s, displayId=%d, config=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_SWITCH",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "374972436": {
       "message": "performEnableScreen: Waiting for anim complete",
       "level": "INFO",
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "385096046": {
-      "message": "Delaying loss of focus...",
-      "level": "INFO",
-      "group": "WM_DEBUG_FOCUS_LIGHT",
-      "at": "com\/android\/server\/wm\/WindowManagerService.java"
-    },
     "399841913": {
       "message": "SURFACE RECOVER DESTROY: %s",
       "level": "INFO",
@@ -1375,6 +1639,12 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimation.java"
     },
+    "487621047": {
+      "message": "DisplayArea vanished name=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+    },
     "490877640": {
       "message": "onStackOrderChanged(): stack=%s",
       "level": "DEBUG",
@@ -1411,6 +1681,12 @@
       "group": "WM_DEBUG_BOOT",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "556758086": {
+      "message": "Applying new update lock state '%s' for %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_IMMERSIVE",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "557227556": {
       "message": "onAnimationFinished(): Notify animation finished:",
       "level": "DEBUG",
@@ -1531,12 +1807,6 @@
       "group": "WM_DEBUG_SCREEN_ON",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "676824470": {
-      "message": "Test completed successfully: %b %d %o %x %e %g %f %% %s.",
-      "level": "ERROR",
-      "group": "TEST_GROUP",
-      "at": "com\/android\/server\/wm\/ProtoLogGroup.java"
-    },
     "685047360": {
       "message": "Resizing window %s",
       "level": "VERBOSE",
@@ -1561,6 +1831,18 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "715749922": {
+      "message": "Allowlisting %d:%s",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
+    "736692676": {
+      "message": "Config is relaunching %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "745391677": {
       "message": "  CREATE SURFACE %s IN SESSION %s: pid=%d format=%d flags=0x%x \/ %s",
       "level": "INFO",
@@ -1615,6 +1897,12 @@
       "group": "WM_DEBUG_RECENTS_ANIMATIONS",
       "at": "com\/android\/server\/wm\/RecentsAnimation.java"
     },
+    "869266572": {
+      "message": "Removing activity %s from stack, reason= %s callers=%s",
+      "level": "INFO",
+      "group": "WM_DEBUG_ADD_REMOVE",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "873914452": {
       "message": "goodToGo()",
       "level": "DEBUG",
@@ -1633,6 +1921,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "906215061": {
+      "message": "Apply window transaction, syncId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
+    },
     "913494177": {
       "message": "removeAllWindowsIfPossible: removing win=%s",
       "level": "WARN",
@@ -1645,12 +1939,30 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowStateAnimator.java"
     },
+    "950074526": {
+      "message": "setLockTaskMode: Can't lock due to auth",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "954470154": {
       "message": "FORCED DISPLAY SCALING DISABLED",
       "level": "INFO",
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "956374481": {
+      "message": "removeLockedTask: task=%s last task, reverting locktask mode. Callers=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
+    "969323241": {
+      "message": "Sending new config to %s, config: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "972354148": {
       "message": "\tcontainer=%s",
       "level": "DEBUG",
@@ -1663,12 +1975,24 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1040675582": {
+      "message": "Can't report activity configuration update - client not running, activityRecord=%s",
+      "level": "WARN",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "1046922686": {
       "message": "requestScrollCapture: caught exception dispatching callback: %s",
       "level": "WARN",
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1049367566": {
+      "message": "Sending to proc %s new config %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/WindowProcessController.java"
+    },
     "1051545910": {
       "message": "Exit animation finished in %s: remove=%b",
       "level": "VERBOSE",
@@ -1681,6 +2005,12 @@
       "group": "WM_DEBUG_REMOTE_ANIMATIONS",
       "at": "com\/android\/server\/wm\/WallpaperAnimationAdapter.java"
     },
+    "1088929964": {
+      "message": "onLockTaskPackagesUpdated: starting new locktask task=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "1089714158": {
       "message": "  FREEZE %s: DESTROY",
       "level": "INFO",
@@ -1705,6 +2035,12 @@
       "group": "WM_DEBUG_SCREEN_ON",
       "at": "com\/android\/server\/wm\/DisplayPolicy.java"
     },
+    "1149424314": {
+      "message": "Unregister display organizer=%s uid=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java"
+    },
     "1160771501": {
       "message": "Resize reasons for w=%s:  %s surfaceResized=%b configChanged=%b dragResizingChanged=%b reportOrientationChanged=%b",
       "level": "VERBOSE",
@@ -1789,6 +2125,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1337596507": {
+      "message": "Sending to proc %s new compat %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/CompatModePackages.java"
+    },
     "1346895820": {
       "message": "ScreenRotation still animating: type: %d\nmDisplayAnimator: %s\nmEnterBlackFrameAnimator: %s\nmRotateScreenAnimator: %s\nmScreenshotRotationAnimator: %s",
       "level": "VERBOSE",
@@ -1801,6 +2143,12 @@
       "group": "WM_DEBUG_FOCUS",
       "at": "com\/android\/server\/wm\/DisplayContent.java"
     },
+    "1360551978": {
+      "message": "Trying to update display configuration for non-existing displayId=%d",
+      "level": "WARN",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "1364498663": {
       "message": "notifyAppResumed: wasStopped=%b %s",
       "level": "VERBOSE",
@@ -1819,6 +2167,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/TaskDisplayArea.java"
     },
+    "1401295262": {
+      "message": "Mode default, asking user",
+      "level": "WARN",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "1401700824": {
       "message": "Window drawn win=%s",
       "level": "DEBUG",
@@ -1927,6 +2281,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1522489371": {
+      "message": "moveActivityStackToFront: activity=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "1525976603": {
       "message": "cancelAnimation(): reason=%s",
       "level": "DEBUG",
@@ -1951,6 +2311,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1576607724": {
+      "message": "Report configuration: %s %s %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "1577579529": {
       "message": "win=%s destroySurfaces: appStopped=%b win.mWindowRemovalAllowed=%b win.mRemoveOnExit=%b",
       "level": "ERROR",
@@ -1981,6 +2347,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1635062046": {
+      "message": "Skipping config check invisible: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "1635462459": {
       "message": "onMovedByResize: Moving %s",
       "level": "DEBUG",
@@ -2023,6 +2395,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1679569477": {
+      "message": "Configuration doesn't matter not running %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "1720229827": {
       "message": "Creating animation bounds layer",
       "level": "INFO",
@@ -2077,12 +2455,30 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "1789603530": {
+      "message": "Removing activity %s hasSavedState=%b stateNotNeeded=%s finishing=%b state=%s callers=%s",
+      "level": "INFO",
+      "group": "WM_DEBUG_ADD_REMOVE",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "1822843721": {
       "message": "Aborted starting %s: startingData=%s",
       "level": "VERBOSE",
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1824105730": {
+      "message": "setLockTaskAuth: task=%s mLockTaskAuth=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/Task.java"
+    },
+    "1829094918": {
+      "message": "onLockTaskPackagesUpdated: removing %s mLockTaskAuth()=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_LOCKTASK",
+      "at": "com\/android\/server\/wm\/LockTaskController.java"
+    },
     "1831008694": {
       "message": "Loading animation for app transition. transit=%s enter=%b frame=%s insets=%s surfaceInsets=%s",
       "level": "DEBUG",
@@ -2131,6 +2527,12 @@
       "group": "WM_DEBUG_ADD_REMOVE",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1918448345": {
+      "message": "Task appeared taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_ORGANIZER",
+      "at": "com\/android\/server\/wm\/TaskOrganizerController.java"
+    },
     "1921821199": {
       "message": "Preserving %s until the new one is added",
       "level": "VERBOSE",
@@ -2161,6 +2563,12 @@
       "group": "WM_DEBUG_APP_TRANSITIONS",
       "at": "com\/android\/server\/wm\/AppTransitionController.java"
     },
+    "1975793405": {
+      "message": "setFocusedStack: stackId=%d",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+    },
     "1984470582": {
       "message": "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d",
       "level": "DEBUG",
@@ -2173,6 +2581,12 @@
       "group": "WM_SHOW_TRANSACTIONS",
       "at": "com\/android\/server\/wm\/WindowAnimator.java"
     },
+    "1995093920": {
+      "message": "Checking to restart %s: changed=0x%s, handles=0x%s, mLastReportedConfiguration=%s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_CONFIGURATION",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "2016061474": {
       "message": "Prepare app transition: transit=%s %s alwaysKeepCurrent=%b displayId=%d Callers=%s",
       "level": "VERBOSE",
@@ -2191,6 +2605,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "2022322588": {
+      "message": "Adding activity %s to stack to task %s callers: %s",
+      "level": "INFO",
+      "group": "WM_DEBUG_ADD_REMOVE",
+      "at": "com\/android\/server\/wm\/Task.java"
+    },
     "2022422429": {
       "message": "createAnimationAdapter(): container=%s",
       "level": "DEBUG",
@@ -2269,6 +2689,12 @@
       "group": "WM_DEBUG_ORIENTATION",
       "at": "com\/android\/server\/wm\/DisplayRotation.java"
     },
+    "2134999275": {
+      "message": "moveActivityStackToFront: already on top, activity=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_FOCUS",
+      "at": "com\/android\/server\/wm\/ActivityRecord.java"
+    },
     "2137411379": {
       "message": "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b Callers=%s",
       "level": "VERBOSE",
@@ -2277,9 +2703,6 @@
     }
   },
   "groups": {
-    "TEST_GROUP": {
-      "tag": "WindowManagetProtoLogTest"
-    },
     "WM_DEBUG_ADD_REMOVE": {
       "tag": "WindowManager"
     },
@@ -2292,6 +2715,12 @@
     "WM_DEBUG_BOOT": {
       "tag": "WindowManager"
     },
+    "WM_DEBUG_CONFIGURATION": {
+      "tag": "WindowManager"
+    },
+    "WM_DEBUG_CONTAINERS": {
+      "tag": "WindowManager"
+    },
     "WM_DEBUG_DRAW": {
       "tag": "WindowManager"
     },
@@ -2304,9 +2733,15 @@
     "WM_DEBUG_IME": {
       "tag": "WindowManager"
     },
+    "WM_DEBUG_IMMERSIVE": {
+      "tag": "WindowManager"
+    },
     "WM_DEBUG_KEEP_SCREEN_ON": {
       "tag": "WindowManager"
     },
+    "WM_DEBUG_LOCKTASK": {
+      "tag": "WindowManager"
+    },
     "WM_DEBUG_ORIENTATION": {
       "tag": "WindowManager"
     },
@@ -2325,9 +2760,15 @@
     "WM_DEBUG_STARTING_WINDOW": {
       "tag": "WindowManager"
     },
+    "WM_DEBUG_SWITCH": {
+      "tag": "WindowManager"
+    },
     "WM_DEBUG_WINDOW_MOVEMENT": {
       "tag": "WindowManager"
     },
+    "WM_DEBUG_WINDOW_ORGANIZER": {
+      "tag": "WindowManager"
+    },
     "WM_ERROR": {
       "tag": "WindowManager"
     },
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index f410490..4f95a53 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -684,15 +684,13 @@
         return b;
     }
 
-    // FIXME: The maxTargetSdk should be R, once R is no longer set to
-    // CUR_DEVELOPMENT.
     /**
      * Creates a new immutable bitmap backed by ashmem which can efficiently
      * be passed between processes.
      *
      * @hide
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R,
             publicAlternatives = "Use {@link #asShared()} instead")
     public Bitmap createAshmemBitmap() {
         checkRecycled("Can't copy a recycled bitmap");
diff --git a/graphics/java/android/graphics/BlurShader.java b/graphics/java/android/graphics/BlurShader.java
new file mode 100644
index 0000000..779a890
--- /dev/null
+++ b/graphics/java/android/graphics/BlurShader.java
@@ -0,0 +1,65 @@
+/*
+ * 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.graphics;
+
+import android.annotation.Nullable;
+
+/**
+ * A subclass of shader that blurs input from another {@link android.graphics.Shader} instance
+ * or all the drawing commands with the {@link android.graphics.Paint} that this shader is
+ * attached to.
+ */
+public final class BlurShader extends Shader {
+
+    private final float mRadiusX;
+    private final float mRadiusY;
+    private final Shader mInputShader;
+
+    private long mNativeInputShader = 0;
+
+    /**
+     * Create a {@link BlurShader} that blurs the contents of the optional input shader
+     * with the specified radius along the x and y axis. If no input shader is provided
+     * then all drawing commands issued with a {@link android.graphics.Paint} that this
+     * shader is installed in will be blurred
+     * @param radiusX Radius of blur along the X axis
+     * @param radiusY Radius of blur along the Y axis
+     * @param inputShader Input shader that provides the content to be blurred
+     */
+    public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader) {
+        mRadiusX = radiusX;
+        mRadiusY = radiusY;
+        mInputShader = inputShader;
+    }
+
+    /** @hide **/
+    @Override
+    protected long createNativeInstance(long nativeMatrix) {
+        mNativeInputShader = mInputShader != null ? mInputShader.getNativeInstance() : 0;
+        return nativeCreate(nativeMatrix, mRadiusX, mRadiusY, mNativeInputShader);
+    }
+
+    /** @hide **/
+    @Override
+    protected boolean shouldDiscardNativeInstance() {
+        long currentNativeInstance = mInputShader != null ? mInputShader.getNativeInstance() : 0;
+        return mNativeInputShader != currentNativeInstance;
+    }
+
+    private static native long nativeCreate(long nativeMatrix, float radiusX, float radiusY,
+            long inputShader);
+}
diff --git a/keystore/TEST_MAPPING b/keystore/TEST_MAPPING
new file mode 100644
index 0000000..0511967
--- /dev/null
+++ b/keystore/TEST_MAPPING
@@ -0,0 +1,74 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsKeystoreTestCases",
+      "options": [
+        {
+          "include-annotation": "android.platform.test.annotations.RequiresDevice"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.SignatureTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.RsaSignaturePerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.RsaKeyGenPerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.RsaCipherPerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.MacTest#testLargeMsgKat"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.KeyPairGeneratorTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.KeyGeneratorTest#testHmacKeySupportedSizes"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.HmacMacPerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.EcdsaSignaturePerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.EcKeyGenPerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.DesCipherPerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.CipherTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.AttestationPerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.AndroidKeyStoreTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.AesCipherPerformanceTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.AESCipherNistCavpKatTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.DESedeECBPKCS7PaddingCipherTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.DESedeECBNoPaddingCipherTest"
+        },
+        {
+          "exclude-filter": "android.keystore.cts.DESedeECBPKCS7PaddingCipherTest"
+        }
+      ]
+    }
+  ],
+  "postsubmit": [
+    {
+      "name": "CtsKeystoreTestCases"
+    }
+  ]
+}
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 843b177..307b82e 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -12,14 +12,88 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// Begin ProtoLog
+java_library {
+    name: "wm_shell_protolog-groups",
+    srcs: [
+        "src/com/android/wm/shell/protolog/ShellProtoLogGroup.java",
+        ":protolog-common-src",
+    ],
+}
+
+filegroup {
+    name: "wm_shell-sources",
+    srcs: ["src/**/*.java"],
+    path: "src",
+}
+
+genrule {
+    name: "wm_shell_protolog_src",
+    srcs: [
+        ":wm_shell_protolog-groups",
+        ":wm_shell-sources",
+    ],
+    tools: ["protologtool"],
+    cmd: "$(location protologtool) transform-protolog-calls " +
+      "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+      "--protolog-impl-class com.android.wm.shell.protolog.ShellProtoLogImpl " +
+      "--protolog-cache-class com.android.wm.shell.protolog.ShellProtoLogCache " +
+      "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
+      "--loggroups-jar $(location :wm_shell_protolog-groups) " +
+      "--output-srcjar $(out) " +
+      "$(locations :wm_shell-sources)",
+    out: ["wm_shell_protolog.srcjar"],
+}
+
+genrule {
+    name: "generate-wm_shell_protolog.json",
+    srcs: [
+        ":wm_shell_protolog-groups",
+        ":wm_shell-sources",
+    ],
+    tools: ["protologtool"],
+    cmd: "$(location protologtool) generate-viewer-config " +
+      "--protolog-class com.android.internal.protolog.common.ProtoLog " +
+      "--loggroups-class com.android.wm.shell.protolog.ShellProtoLogGroup " +
+      "--loggroups-jar $(location :wm_shell_protolog-groups) " +
+      "--viewer-conf $(out) " +
+      "$(locations :wm_shell-sources)",
+    out: ["wm_shell_protolog.json"],
+}
+
+filegroup {
+    name: "wm_shell_protolog.json",
+    srcs: ["res/raw/wm_shell_protolog.json"],
+}
+
+genrule {
+    name: "checked-wm_shell_protolog.json",
+    srcs: [
+        ":generate-wm_shell_protolog.json",
+        ":wm_shell_protolog.json",
+    ],
+    cmd: "cp $(location :generate-wm_shell_protolog.json) $(out) && " +
+      "{ ! (diff $(out) $(location :wm_shell_protolog.json) | grep -q '^<') || " +
+      "{ echo -e '\\n\\n################################################################\\n#\\n" +
+      "#  ERROR: ProtoLog viewer config is stale.  To update it, run:\\n#\\n" +
+      "#  cp $(location :generate-wm_shell_protolog.json) " +
+      "$(location :wm_shell_protolog.json)\\n#\\n" +
+      "################################################################\\n\\n' >&2 && false; } }",
+    out: ["wm_shell_protolog.json"],
+}
+// End ProtoLog
+
 android_library {
     name: "WindowManager-Shell",
     srcs: [
-        "src/**/*.java",
+        ":wm_shell_protolog_src",
         "src/**/I*.aidl",
     ],
     resource_dirs: [
         "res",
     ],
+    static_libs: [
+        "protolog-lib",
+    ],
     manifest: "AndroidManifest.xml",
-}
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml b/libs/WindowManager/Shell/res/layout/pip_menu.xml
similarity index 100%
rename from libs/WindowManager/Shell/res/layout/pip_menu_activity.xml
rename to libs/WindowManager/Shell/res/layout/pip_menu.xml
diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
new file mode 100644
index 0000000..7242793
--- /dev/null
+++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json
@@ -0,0 +1,46 @@
+{
+  "version": "1.0.0",
+  "messages": {
+    "-1340279385": {
+      "message": "Remove listener=%s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
+    "-880817403": {
+      "message": "Task vanished taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
+    "-460572385": {
+      "message": "Task appeared taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
+    "-242812822": {
+      "message": "Add listener for modes=%s listener=%s",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
+    "157713005": {
+      "message": "Task info changed taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    },
+    "980952660": {
+      "message": "Task root back pressed taskId=%d",
+      "level": "VERBOSE",
+      "group": "WM_SHELL_TASK_ORG",
+      "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java"
+    }
+  },
+  "groups": {
+    "WM_SHELL_TASK_ORG": {
+      "tag": "WindowManagerShell"
+    }
+  }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
new file mode 100644
index 0000000..ea9576a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -0,0 +1,175 @@
+/*
+ * 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.wm.shell;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.view.SurfaceControl;
+import android.window.TaskOrganizer;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import com.android.wm.shell.protolog.ShellProtoLogImpl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Unified task organizer for all components in the shell.
+ */
+public class ShellTaskOrganizer extends TaskOrganizer {
+
+    private static final String TAG = "ShellTaskOrganizer";
+
+    /**
+     * Callbacks for when the tasks change in the system.
+     */
+    public interface TaskListener {
+        default void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {}
+        default void onTaskInfoChanged(RunningTaskInfo taskInfo) {}
+        default void onTaskVanished(RunningTaskInfo taskInfo) {}
+        default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {}
+    }
+
+    private final SparseArray<ArrayList<TaskListener>> mListenersByWindowingMode =
+            new SparseArray<>();
+
+    // Keeps track of all the tasks reported to this organizer (changes in windowing mode will
+    // require us to report to both old and new listeners)
+    private final SparseArray<Pair<RunningTaskInfo, SurfaceControl>> mTasks = new SparseArray<>();
+
+    /**
+     * Adds a listener for tasks in a specific windowing mode.
+     */
+    public void addListener(TaskListener listener, int... windowingModes) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Add listener for modes=%s listener=%s",
+                Arrays.toString(windowingModes), listener);
+        for (int winMode : windowingModes) {
+            ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(winMode);
+            if (listeners == null) {
+                listeners = new ArrayList<>();
+                mListenersByWindowingMode.put(winMode, listeners);
+            }
+            if (listeners.contains(listener)) {
+                Log.w(TAG, "Listener already exists");
+                return;
+            }
+            listeners.add(listener);
+
+            // Notify the listener of all existing tasks in that windowing mode
+            for (int i = mTasks.size() - 1; i >= 0; i--) {
+                Pair<RunningTaskInfo, SurfaceControl> data = mTasks.valueAt(i);
+                int taskWinMode = data.first.configuration.windowConfiguration.getWindowingMode();
+                if (taskWinMode == winMode) {
+                    listener.onTaskAppeared(data.first, data.second);
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes a registered listener.
+     */
+    public void removeListener(TaskListener listener) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Remove listener=%s", listener);
+        for (int i = 0; i < mListenersByWindowingMode.size(); i++) {
+            mListenersByWindowingMode.valueAt(i).remove(listener);
+        }
+    }
+
+    @Override
+    public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task appeared taskId=%d",
+                taskInfo.taskId);
+        mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, leash));
+        ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(
+                getWindowingMode(taskInfo));
+        if (listeners != null) {
+            for (int i = listeners.size() - 1; i >= 0; i--) {
+                listeners.get(i).onTaskAppeared(taskInfo, leash);
+            }
+        }
+    }
+
+    @Override
+    public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task info changed taskId=%d",
+                taskInfo.taskId);
+        Pair<RunningTaskInfo, SurfaceControl> data = mTasks.get(taskInfo.taskId);
+        int winMode = getWindowingMode(taskInfo);
+        int prevWinMode = getWindowingMode(data.first);
+        if (prevWinMode != -1 && prevWinMode != winMode) {
+            // TODO: We currently send vanished/appeared as the task moves between win modes, but
+            //       we should consider adding a different mode-changed callback
+            ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(prevWinMode);
+            if (listeners != null) {
+                for (int i = listeners.size() - 1; i >= 0; i--) {
+                    listeners.get(i).onTaskVanished(taskInfo);
+                }
+            }
+            listeners = mListenersByWindowingMode.get(winMode);
+            if (listeners != null) {
+                SurfaceControl leash = data.second;
+                for (int i = listeners.size() - 1; i >= 0; i--) {
+                    listeners.get(i).onTaskAppeared(taskInfo, leash);
+                }
+            }
+        } else {
+            ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(winMode);
+            if (listeners != null) {
+                for (int i = listeners.size() - 1; i >= 0; i--) {
+                    listeners.get(i).onTaskInfoChanged(taskInfo);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d",
+                taskInfo.taskId);
+        ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(
+                getWindowingMode(taskInfo));
+        if (listeners != null) {
+            for (int i = listeners.size() - 1; i >= 0; i--) {
+                listeners.get(i).onBackPressedOnTaskRoot(taskInfo);
+            }
+        }
+    }
+
+    @Override
+    public void onTaskVanished(RunningTaskInfo taskInfo) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task vanished taskId=%d",
+                taskInfo.taskId);
+        int prevWinMode = getWindowingMode(mTasks.get(taskInfo.taskId).first);
+        mTasks.remove(taskInfo.taskId);
+        ArrayList<TaskListener> listeners = mListenersByWindowingMode.get(prevWinMode);
+        if (listeners != null) {
+            for (int i = listeners.size() - 1; i >= 0; i--) {
+                listeners.get(i).onTaskVanished(taskInfo);
+            }
+        }
+    }
+
+    private int getWindowingMode(RunningTaskInfo taskInfo) {
+        return taskInfo.configuration.windowConfiguration.getWindowingMode();
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index aeda2d9..283fd8d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -68,7 +68,7 @@
     private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>();
     private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>();
 
-    protected DisplayImeController(IWindowManager wmService, DisplayController displayController,
+    public DisplayImeController(IWindowManager wmService, DisplayController displayController,
             Handler mainHandler, TransactionPool transactionPool) {
         mHandler = mainHandler;
         mWmService = wmService;
@@ -76,7 +76,8 @@
         mDisplayController = displayController;
     }
 
-    protected void startMonitorDisplays() {
+    /** Starts monitor displays changes and set insets controller for each displays. */
+    public void startMonitorDisplays() {
         mDisplayController.addDisplayWindowListener(this);
     }
 
@@ -493,29 +494,4 @@
         return IInputMethodManager.Stub.asInterface(
                 ServiceManager.getService(Context.INPUT_METHOD_SERVICE));
     }
-
-    /** Builds {@link DisplayImeController} instance. */
-    public static class Builder {
-        private IWindowManager mWmService;
-        private DisplayController mDisplayController;
-        private Handler mHandler;
-        private TransactionPool mTransactionPool;
-
-        public Builder(IWindowManager wmService, DisplayController displayController,
-                Handler handler, TransactionPool transactionPool) {
-            mWmService = wmService;
-            mDisplayController = displayController;
-            mHandler = handler;
-            mTransactionPool = transactionPool;
-        }
-
-        /** Builds and initializes {@link DisplayImeController} instance. */
-        public DisplayImeController build() {
-            DisplayImeController displayImeController = new DisplayImeController(mWmService,
-                    mDisplayController, mHandler, mTransactionPool);
-            // Separates startMonitorDisplays from constructor to prevent circular init issue.
-            displayImeController.startMonitorDisplays();
-            return displayImeController;
-        }
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
new file mode 100644
index 0000000..ae09754
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -0,0 +1,92 @@
+/*
+ * 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.wm.shell.protolog;
+
+import com.android.internal.protolog.common.IProtoLogGroup;
+
+/**
+ * Defines logging groups for ProtoLog.
+ *
+ * This file is used by the ProtoLogTool to generate optimized logging code.
+ */
+public enum ShellProtoLogGroup implements IProtoLogGroup {
+    WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+            Consts.TAG_WM_SHELL),
+    TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
+
+    private final boolean mEnabled;
+    private volatile boolean mLogToProto;
+    private volatile boolean mLogToLogcat;
+    private final String mTag;
+
+    /**
+     * @param enabled     set to false to exclude all log statements for this group from
+     *                    compilation,
+     *                    they will not be available in runtime.
+     * @param logToProto  enable binary logging for the group
+     * @param logToLogcat enable text logging for the group
+     * @param tag         name of the source of the logged message
+     */
+    ShellProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) {
+        this.mEnabled = enabled;
+        this.mLogToProto = logToProto;
+        this.mLogToLogcat = logToLogcat;
+        this.mTag = tag;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
+    @Override
+    public boolean isLogToProto() {
+        return mLogToProto;
+    }
+
+    @Override
+    public boolean isLogToLogcat() {
+        return mLogToLogcat;
+    }
+
+    @Override
+    public boolean isLogToAny() {
+        return mLogToLogcat || mLogToProto;
+    }
+
+    @Override
+    public String getTag() {
+        return mTag;
+    }
+
+    @Override
+    public void setLogToProto(boolean logToProto) {
+        this.mLogToProto = logToProto;
+    }
+
+    @Override
+    public void setLogToLogcat(boolean logToLogcat) {
+        this.mLogToLogcat = logToLogcat;
+    }
+
+    private static class Consts {
+        private static final String TAG_WM_SHELL = "WindowManagerShell";
+
+        private static final boolean ENABLE_DEBUG = true;
+        private static final boolean ENABLE_LOG_TO_PROTO_DEBUG = true;
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
new file mode 100644
index 0000000..6a925e7
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
@@ -0,0 +1,138 @@
+/*
+ * 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.wm.shell.protolog;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.BaseProtoLogImpl;
+import com.android.internal.protolog.ProtoLogViewerConfigReader;
+import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.wm.shell.R;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import org.json.JSONException;
+
+
+/**
+ * A service for the ProtoLog logging system.
+ */
+public class ShellProtoLogImpl extends BaseProtoLogImpl {
+    private static final String TAG = "ProtoLogImpl";
+    private static final int BUFFER_CAPACITY = 1024 * 1024;
+    // TODO: Get the right path for the proto log file when we initialize the shell components
+    private static final String LOG_FILENAME = new File("wm_shell_log.pb").getAbsolutePath();
+
+    private static ShellProtoLogImpl sServiceInstance = null;
+
+    private final PrintWriter mSystemOutWriter;
+
+    static {
+        addLogGroupEnum(ShellProtoLogGroup.values());
+    }
+
+    /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+    public static void d(IProtoLogGroup group, int messageHash, int paramsMask,
+            @Nullable String messageString,
+            Object... args) {
+        getSingleInstance()
+                .log(LogLevel.DEBUG, group, messageHash, paramsMask, messageString, args);
+    }
+
+    /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+    public static void v(IProtoLogGroup group, int messageHash, int paramsMask,
+            @Nullable String messageString,
+            Object... args) {
+        getSingleInstance().log(LogLevel.VERBOSE, group, messageHash, paramsMask, messageString,
+                args);
+    }
+
+    /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+    public static void i(IProtoLogGroup group, int messageHash, int paramsMask,
+            @Nullable String messageString,
+            Object... args) {
+        getSingleInstance().log(LogLevel.INFO, group, messageHash, paramsMask, messageString, args);
+    }
+
+    /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+    public static void w(IProtoLogGroup group, int messageHash, int paramsMask,
+            @Nullable String messageString,
+            Object... args) {
+        getSingleInstance().log(LogLevel.WARN, group, messageHash, paramsMask, messageString, args);
+    }
+
+    /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+    public static void e(IProtoLogGroup group, int messageHash, int paramsMask,
+            @Nullable String messageString,
+            Object... args) {
+        getSingleInstance()
+                .log(LogLevel.ERROR, group, messageHash, paramsMask, messageString, args);
+    }
+
+    /** Used by the ProtoLogTool, do not call directly - use {@code ProtoLog} class instead. */
+    public static void wtf(IProtoLogGroup group, int messageHash, int paramsMask,
+            @Nullable String messageString,
+            Object... args) {
+        getSingleInstance().log(LogLevel.WTF, group, messageHash, paramsMask, messageString, args);
+    }
+
+    /** Returns true iff logging is enabled for the given {@code IProtoLogGroup}. */
+    public static boolean isEnabled(IProtoLogGroup group) {
+        return group.isLogToLogcat()
+                || (group.isLogToProto() && getSingleInstance().isProtoEnabled());
+    }
+
+    /**
+     * Returns the single instance of the ProtoLogImpl singleton class.
+     */
+    public static synchronized ShellProtoLogImpl getSingleInstance() {
+        if (sServiceInstance == null) {
+            sServiceInstance = new ShellProtoLogImpl();
+        }
+        return sServiceInstance;
+    }
+
+    public void startTextLogging(Context context, String... groups) {
+        try {
+            mViewerConfig.loadViewerConfig(
+                    context.getResources().openRawResource(R.raw.wm_shell_protolog));
+            setLogging(true /* setTextLogging */, true, mSystemOutWriter, groups);
+        } catch (IOException e) {
+            Log.i(TAG, "Unable to load log definitions: IOException while reading "
+                    + "wm_shell_protolog. " + e);
+        } catch (JSONException e) {
+            Log.i(TAG, "Unable to load log definitions: JSON parsing exception while reading "
+                    + "wm_shell_protolog. " + e);
+        }
+    }
+
+    public void stopTextLogging(String... groups) {
+        setLogging(true /* setTextLogging */, false, mSystemOutWriter, groups);
+    }
+
+    private ShellProtoLogImpl() {
+        super(new File(LOG_FILENAME), null, BUFFER_CAPACITY,
+                new ProtoLogViewerConfigReader());
+        mSystemOutWriter = new PrintWriter(System.out, true);
+    }
+}
+
diff --git a/libs/WindowManager/Shell/tests/README.md b/libs/WindowManager/Shell/tests/README.md
new file mode 100644
index 0000000..c19db76
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/README.md
@@ -0,0 +1,15 @@
+# WM Shell Test
+
+This contains all tests written for WM (WindowManager) Shell and it's currently
+divided into 3 categories
+
+- unittest, tests against individual functions, usually @SmallTest and do not
+  require UI automation nor real device to run
+- integration, this maybe a mix of functional and integration tests. Contains
+  tests verify the WM Shell as a whole, like talking to WM core. This usually
+  involves mocking the window manager service or even talking to the real one.
+  Due to this nature, test cases in this package is normally annotated as
+  @LargeTest and runs with UI automation on real device
+- flicker, similar to functional tests with its sole focus on flickerness. See
+  [WM Shell Flicker Test Package](http://cs/android/framework/base/libs/WindowManager/Shell/tests/flicker/)
+  for more details
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
new file mode 100644
index 0000000..5879022
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -0,0 +1,34 @@
+//
+// 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.
+//
+
+android_test {
+    name: "WMShellFlickerTests",
+    srcs: ["src/**/*.java", "src/**/*.kt"],
+    manifest: "AndroidManifest.xml",
+    test_config: "AndroidTest.xml",
+    platform_apis: true,
+    certificate: "platform",
+    test_suites: ["device-tests"],
+    libs: ["android.test.runner"],
+    static_libs: [
+        "androidx.test.ext.junit",
+        "flickerlib",
+        "truth-prebuilt",
+        "app-helpers-core",
+        "launcher-helper-lib",
+        "launcher-aosp-tapl"
+    ],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
new file mode 100644
index 0000000..8b2f668
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.wm.shell.flicker">
+
+    <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+    <!-- Read and write traces from external storage -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <!-- Write secure settings -->
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+    <!-- Capture screen contents -->
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+    <!-- Enable / Disable tracing !-->
+    <uses-permission android:name="android.permission.DUMP" />
+    <!-- Run layers trace -->
+    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+    <!-- Workaround grant runtime permission exception from b/152733071 -->
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+    <uses-permission android:name="android.permission.READ_LOGS"/>
+    <application>
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.wm.shell.flicker"
+                     android:label="WindowManager Shell Flicker Tests">
+    </instrumentation>
+</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
new file mode 100644
index 0000000..526fc50
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2020 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Runs WindowManager Shell Flicker Tests">
+    <option name="test-tag" value="FlickerTests" />
+    <target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on" />
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true" />
+        <!-- set WM tracing verbose level to all -->
+        <option name="run-command" value="cmd window tracing level all" />
+        <!-- inform WM to log all transactions -->
+        <option name="run-command" value="cmd window tracing transaction" />
+        <!-- restart launcher to activate TAPL -->
+        <option name="run-command" value="setprop ro.test_harness 1 ; am force-stop com.google.android.apps.nexuslauncher" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
+        <!-- reboot the device to teardown any crashed tests -->
+        <option name="cleanup-action" value="REBOOT" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="WMShellFlickerTests.apk"/>
+        <option name="test-file-name" value="WMShellFlickerTestApp.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.wm.shell.flicker"/>
+        <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
+        <option name="shell-timeout" value="6600s" />
+        <option name="test-timeout" value="6000s" />
+        <option name="hidden-api-checks" value="false" />
+    </test>
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/storage/emulated/0/Android/data/com.android.wm.shell.flicker/files" />
+        <option name="collect-on-run-ended-only" value="true" />
+        <option name="clean-up" value="true" />
+    </metrics_collector>
+</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/README.md b/libs/WindowManager/Shell/tests/flicker/README.md
new file mode 100644
index 0000000..4502d49
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/README.md
@@ -0,0 +1,10 @@
+# WM Shell Flicker Test Package
+
+Please reference the following links
+
+- [Introduction to Flicker Test Library](http://cs/android/platform_testing/libraries/flicker/)
+- [Flicker Test in frameworks/base](http://cs/android/frameworks/base/tests/FlickerTests/)
+
+on what is Flicker Test and how to write a Flicker Test
+
+To run the Flicker Tests for WM Shell, simply run `atest WMShellFlickerTests`
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
new file mode 100644
index 0000000..4ff2bfc
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.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.wm.shell.flicker
+
+import com.android.server.wm.flicker.dsl.EventLogAssertion
+import com.android.server.wm.flicker.dsl.LayersAssertion
+import com.android.server.wm.flicker.dsl.WmAssertion
+import com.android.server.wm.flicker.helpers.WindowUtils
+
+@JvmOverloads
+fun WmAssertion.statusBarWindowIsAlwaysVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("statusBarWindowIsAlwaysVisible", enabled, bugId) {
+        this.showsAboveAppWindow(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
+    }
+}
+
+@JvmOverloads
+fun WmAssertion.navBarWindowIsAlwaysVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("navBarWindowIsAlwaysVisible", enabled, bugId) {
+        this.showsAboveAppWindow(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertion.noUncoveredRegions(
+    beginRotation: Int,
+    endRotation: Int = beginRotation,
+    allStates: Boolean = true,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    val startingBounds = WindowUtils.getDisplayBounds(beginRotation)
+    val endingBounds = WindowUtils.getDisplayBounds(endRotation)
+    if (allStates) {
+        all("noUncoveredRegions", enabled, bugId) {
+            if (startingBounds == endingBounds) {
+                this.coversAtLeastRegion(startingBounds)
+            } else {
+                this.coversAtLeastRegion(startingBounds)
+                        .then()
+                        .coversAtLeastRegion(endingBounds)
+            }
+        }
+    } else {
+        start("noUncoveredRegions_StartingPos") {
+            this.coversAtLeastRegion(startingBounds)
+        }
+        end("noUncoveredRegions_EndingPos") {
+            this.coversAtLeastRegion(endingBounds)
+        }
+    }
+}
+
+@JvmOverloads
+fun LayersAssertion.navBarLayerIsAlwaysVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("navBarLayerIsAlwaysVisible", enabled, bugId) {
+        this.showsLayer(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertion.statusBarLayerIsAlwaysVisible(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all("statusBarLayerIsAlwaysVisible", enabled, bugId) {
+        this.showsLayer(FlickerTestBase.STATUS_BAR_WINDOW_TITLE)
+    }
+}
+
+@JvmOverloads
+fun LayersAssertion.navBarLayerRotatesAndScales(
+    beginRotation: Int,
+    endRotation: Int = beginRotation,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    val startingPos = WindowUtils.getNavigationBarPosition(beginRotation)
+    val endingPos = WindowUtils.getNavigationBarPosition(endRotation)
+
+    start("navBarLayerRotatesAndScales_StartingPos", enabled, bugId) {
+        this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+    }
+    end("navBarLayerRotatesAndScales_EndingPost", enabled, bugId) {
+        this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, endingPos)
+    }
+
+    if (startingPos == endingPos) {
+        all("navBarLayerRotatesAndScales", enabled, bugId) {
+            this.hasVisibleRegion(FlickerTestBase.NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+        }
+    }
+}
+
+@JvmOverloads
+fun LayersAssertion.statusBarLayerRotatesScales(
+    beginRotation: Int,
+    endRotation: Int = beginRotation,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    val startingPos = WindowUtils.getStatusBarPosition(beginRotation)
+    val endingPos = WindowUtils.getStatusBarPosition(endRotation)
+
+    start("statusBarLayerRotatesScales_StartingPos", enabled, bugId) {
+        this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, startingPos)
+    }
+    end("statusBarLayerRotatesScales_EndingPos", enabled, bugId) {
+        this.hasVisibleRegion(FlickerTestBase.STATUS_BAR_WINDOW_TITLE, endingPos)
+    }
+}
+
+fun EventLogAssertion.focusChanges(
+    vararg windows: String,
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all(enabled = enabled, bugId = bugId) {
+        this.focusChanges(windows)
+    }
+}
+
+fun EventLogAssertion.focusDoesNotChange(
+    bugId: Int = 0,
+    enabled: Boolean = bugId == 0
+) {
+    all(enabled = enabled, bugId = bugId) {
+        this.focusDoesNotChange()
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
new file mode 100644
index 0000000..99f824b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/FlickerTestBase.kt
@@ -0,0 +1,130 @@
+/*
+ * 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.wm.shell.flicker
+
+import android.os.RemoteException
+import android.os.SystemClock
+import android.platform.helpers.IAppHelper
+import android.view.Surface
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.server.wm.flicker.Flicker
+
+/**
+ * Base class of all Flicker test that performs common functions for all flicker tests:
+ *
+ *
+ * - Caches transitions so that a transition is run once and the transition results are used by
+ * tests multiple times. This is needed for parameterized tests which call the BeforeClass methods
+ * multiple times.
+ * - Keeps track of all test artifacts and deletes ones which do not need to be reviewed.
+ * - Fails tests if results are not available for any test due to jank.
+ */
+abstract class FlickerTestBase {
+    val instrumentation by lazy {
+        InstrumentationRegistry.getInstrumentation()
+    }
+    val uiDevice by lazy {
+        UiDevice.getInstance(instrumentation)
+    }
+
+    /**
+     * Build a test tag for the test
+     * @param testName Name of the transition(s) being tested
+     * @param app App being launcher
+     * @param rotation Initial screen rotation
+     *
+     * @return test tag with pattern <NAME>__<APP>__<ROTATION>
+    </ROTATION></APP></NAME> */
+    protected fun buildTestTag(testName: String, app: IAppHelper, rotation: Int): String {
+        return buildTestTag(
+                testName, app, rotation, rotation, app2 = null, extraInfo = "")
+    }
+
+    /**
+     * Build a test tag for the test
+     * @param testName Name of the transition(s) being tested
+     * @param app App being launcher
+     * @param beginRotation Initial screen rotation
+     * @param endRotation End screen rotation (if any, otherwise use same as initial)
+     *
+     * @return test tag with pattern <NAME>__<APP>__<BEGIN_ROTATION>-<END_ROTATION>
+    </END_ROTATION></BEGIN_ROTATION></APP></NAME> */
+    protected fun buildTestTag(
+        testName: String,
+        app: IAppHelper,
+        beginRotation: Int,
+        endRotation: Int
+    ): String {
+        return buildTestTag(
+                testName, app, beginRotation, endRotation, app2 = null, extraInfo = "")
+    }
+
+    /**
+     * Build a test tag for the test
+     * @param testName Name of the transition(s) being tested
+     * @param app App being launcher
+     * @param app2 Second app being launched (if any)
+     * @param beginRotation Initial screen rotation
+     * @param endRotation End screen rotation (if any, otherwise use same as initial)
+     * @param extraInfo Additional information to append to the tag
+     *
+     * @return test tag with pattern <NAME>__<APP></APP>(S)>__<ROTATION></ROTATION>(S)>[__<EXTRA>]
+    </EXTRA></NAME> */
+    protected fun buildTestTag(
+        testName: String,
+        app: IAppHelper,
+        beginRotation: Int,
+        endRotation: Int,
+        app2: IAppHelper?,
+        extraInfo: String
+    ): String {
+        var testTag = "${testName}__${app.launcherName}"
+        if (app2 != null) {
+            testTag += "-${app2.launcherName}"
+        }
+        testTag += "__${Surface.rotationToString(beginRotation)}"
+        if (endRotation != beginRotation) {
+            testTag += "-${Surface.rotationToString(endRotation)}"
+        }
+        if (extraInfo.isNotEmpty()) {
+            testTag += "__$extraInfo"
+        }
+        return testTag
+    }
+
+    protected fun Flicker.setRotation(rotation: Int) {
+        try {
+            when (rotation) {
+                Surface.ROTATION_270 -> device.setOrientationLeft()
+                Surface.ROTATION_90 -> device.setOrientationRight()
+                Surface.ROTATION_0 -> device.setOrientationNatural()
+                else -> device.setOrientationNatural()
+            }
+            // Wait for animation to complete
+            SystemClock.sleep(1000)
+        } catch (e: RemoteException) {
+            throw RuntimeException(e)
+        }
+    }
+
+    companion object {
+        const val NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar"
+        const val STATUS_BAR_WINDOW_TITLE = "StatusBar"
+        const val DOCKED_STACK_DIVIDER = "DockedStackDivider"
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NonRotationTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NonRotationTestBase.kt
new file mode 100644
index 0000000..90334ae
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/NonRotationTestBase.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.wm.shell.flicker
+
+import android.view.Surface
+import org.junit.runners.Parameterized
+
+abstract class NonRotationTestBase(
+    protected val rotationName: String,
+    protected val rotation: Int
+) : FlickerTestBase() {
+    companion object {
+        const val SCREENSHOT_LAYER = "RotationLayer"
+
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<Array<Any>> {
+            val supportedRotations = intArrayOf(Surface.ROTATION_0, Surface.ROTATION_90)
+            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FlickerAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FlickerAppHelper.kt
new file mode 100644
index 0000000..308a36e
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FlickerAppHelper.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.wm.shell.flicker.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import com.android.server.wm.flicker.StandardAppHelper
+
+abstract class FlickerAppHelper(
+    instr: Instrumentation,
+    launcherName: String,
+    launcherStrategy: ILauncherStrategy
+) : StandardAppHelper(instr, sFlickerPackage, launcherName, launcherStrategy) {
+    companion object {
+        var sFlickerPackage = "com.android.wm.shell.flicker.testapp"
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
new file mode 100644
index 0000000..5391702
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
@@ -0,0 +1,45 @@
+/*
+ * 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.wm.shell.flicker.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.flicker.helpers.closePipWindow
+import org.junit.Assert
+
+class PipAppHelper(
+    instr: Instrumentation,
+    launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+            .getInstance(instr)
+            .launcherStrategy
+) : FlickerAppHelper(instr, "PipApp", launcherStrategy) {
+    fun clickEnterPipButton(device: UiDevice) {
+        val enterPipButton = device.findObject(By.res(getPackage(), "enter_pip"))
+        Assert.assertNotNull("Pip button not found, this usually happens when the device " +
+                "was left in an unknown state (e.g. in split screen)", enterPipButton)
+        enterPipButton.click()
+        device.hasPipWindow()
+    }
+
+    fun closePipWindow(device: UiDevice) {
+        device.closePipWindow()
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
new file mode 100644
index 0000000..4b04449
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt
@@ -0,0 +1,121 @@
+/*
+ * 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.wm.shell.flicker.pip
+
+import android.view.Surface
+import androidx.test.filters.FlakyTest
+import androidx.test.filters.LargeTest
+import com.android.server.wm.flicker.dsl.flicker
+import com.android.server.wm.flicker.helpers.closePipWindow
+import com.android.server.wm.flicker.helpers.expandPipWindow
+import com.android.server.wm.flicker.helpers.hasPipWindow
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.wm.shell.flicker.navBarLayerIsAlwaysVisible
+import com.android.wm.shell.flicker.navBarLayerRotatesAndScales
+import com.android.wm.shell.flicker.navBarWindowIsAlwaysVisible
+import com.android.wm.shell.flicker.noUncoveredRegions
+import com.android.wm.shell.flicker.statusBarLayerIsAlwaysVisible
+import com.android.wm.shell.flicker.statusBarLayerRotatesScales
+import com.android.wm.shell.flicker.statusBarWindowIsAlwaysVisible
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test Pip launch.
+ * To run this test: `atest FlickerTests:PipToAppTest`
+ */
+@LargeTest
+@RunWith(Parameterized::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@FlakyTest(bugId = 152738416)
+class EnterPipTest(
+    rotationName: String,
+    rotation: Int
+) : PipTestBase(rotationName, rotation) {
+    @Test
+    fun test() {
+        flicker(instrumentation) {
+            withTag { buildTestTag("enterPip", testApp, rotation) }
+            repeat { 1 }
+            setup {
+                test {
+                    device.wakeUpAndGoToHomeScreen()
+                }
+                eachRun {
+                    device.pressHome()
+                    testApp.open()
+                    this.setRotation(rotation)
+                }
+            }
+            teardown {
+                eachRun {
+                    if (device.hasPipWindow()) {
+                        device.closePipWindow()
+                    }
+                    testApp.exit()
+                    this.setRotation(Surface.ROTATION_0)
+                }
+                test {
+                    if (device.hasPipWindow()) {
+                        device.closePipWindow()
+                    }
+                }
+            }
+            transitions {
+                testApp.clickEnterPipButton(device)
+                device.expandPipWindow()
+            }
+            assertions {
+                windowManagerTrace {
+                    navBarWindowIsAlwaysVisible()
+                    statusBarWindowIsAlwaysVisible()
+                    all("pipWindowBecomesVisible") {
+                        this.showsAppWindow(testApp.`package`)
+                                .then()
+                                .showsAppWindow(sPipWindowTitle)
+                    }
+                }
+
+                layersTrace {
+                    navBarLayerIsAlwaysVisible()
+                    statusBarLayerIsAlwaysVisible()
+                    noUncoveredRegions(rotation, Surface.ROTATION_0, allStates = false)
+                    navBarLayerRotatesAndScales(rotation, Surface.ROTATION_0)
+                    statusBarLayerRotatesScales(rotation, Surface.ROTATION_0)
+
+                    all("pipLayerBecomesVisible") {
+                        this.showsLayer(testApp.launcherName)
+                                .then()
+                                .showsLayer(sPipWindowTitle)
+                    }
+                }
+            }
+        }
+    }
+
+    companion object {
+        @Parameterized.Parameters(name = "{0}")
+        @JvmStatic
+        fun getParams(): Collection<Array<Any>> {
+            val supportedRotations = intArrayOf(Surface.ROTATION_0)
+            return supportedRotations.map { arrayOf(Surface.rotationToString(it), it) }
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt
similarity index 60%
copy from libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java
copy to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt
index f1ead3c..3822d69 100644
--- a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTestBase.kt
@@ -14,25 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell;
+package com.android.wm.shell.flicker.pip
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
+import com.android.wm.shell.flicker.NonRotationTestBase
+import com.android.wm.shell.flicker.helpers.PipAppHelper
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
+abstract class PipTestBase(
+    rotationName: String,
+    rotation: Int
+) : NonRotationTestBase(rotationName, rotation) {
+    protected val testApp = PipAppHelper(instrumentation)
 
-/**
- * Tests for the shell.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class WindowManagerShellTest {
-
-    WindowManagerShell mShell;
-
-    @Test
-    public void testNothing() {
-        // Do nothing
+    companion object {
+        const val sPipWindowTitle = "PipMenuActivity"
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/Android.bp b/libs/WindowManager/Shell/tests/flicker/test-apps/Android.bp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/Android.bp
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp
new file mode 100644
index 0000000..d12b492
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp
@@ -0,0 +1,20 @@
+// 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.
+
+android_test {
+    name: "WMShellFlickerTestApp",
+    srcs: ["**/*.java"],
+    sdk_version: "current",
+    test_suites: ["device-tests"],
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
new file mode 100644
index 0000000..95dc1d4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+     package="com.android.wm.shell.flicker.testapp">
+
+    <uses-sdk android:minSdkVersion="29"
+         android:targetSdkVersion="29"/>
+    <application android:allowBackup="false"
+         android:supportsRtl="true">
+        <activity android:name=".PipActivity"
+             android:resizeableActivity="true"
+             android:supportsPictureInPicture="true"
+             android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
+             android:taskAffinity="com.android.wm.shell.flicker.testapp.PipActivity"
+             android:label="PipApp"
+             android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml
new file mode 100644
index 0000000..e1870d9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/holo_blue_bright">
+    <Button android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/enter_pip"
+            android:text="Enter PIP"/>
+</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java
new file mode 100644
index 0000000..3052816
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java
@@ -0,0 +1,45 @@
+/*
+ * 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.wm.shell.flicker.testapp;
+
+import android.app.Activity;
+import android.app.PictureInPictureParams;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.Rational;
+import android.view.WindowManager;
+import android.widget.Button;
+
+public class PipActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        WindowManager.LayoutParams p = getWindow().getAttributes();
+        p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+                .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        getWindow().setAttributes(p);
+        setContentView(R.layout.activity_pip);
+        Button enterPip = (Button) findViewById(R.id.enter_pip);
+
+        PictureInPictureParams params = new PictureInPictureParams.Builder()
+                .setAspectRatio(new Rational(1, 1))
+                .setSourceRectHint(new Rect(0, 0, 100, 100))
+                .build();
+
+        enterPip.setOnClickListener((v) -> enterPictureInPictureMode(params));
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
similarity index 96%
rename from libs/WindowManager/Shell/tests/Android.bp
rename to libs/WindowManager/Shell/tests/unittest/Android.bp
index 9868879..692e2fa 100644
--- a/libs/WindowManager/Shell/tests/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 android_test {
-    name: "WindowManagerShellTests",
+    name: "WMShellUnitTests",
 
     srcs: ["**/*.java"],
 
diff --git a/libs/WindowManager/Shell/tests/AndroidManifest.xml b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
similarity index 100%
rename from libs/WindowManager/Shell/tests/AndroidManifest.xml
rename to libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
diff --git a/libs/WindowManager/Shell/tests/AndroidTest.xml b/libs/WindowManager/Shell/tests/unittest/AndroidTest.xml
similarity index 90%
rename from libs/WindowManager/Shell/tests/AndroidTest.xml
rename to libs/WindowManager/Shell/tests/unittest/AndroidTest.xml
index 4dce4db..21ed2c0 100644
--- a/libs/WindowManager/Shell/tests/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/unittest/AndroidTest.xml
@@ -17,12 +17,12 @@
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="install-arg" value="-t" />
-        <option name="test-file-name" value="WindowManagerShellTests.apk" />
+        <option name="test-file-name" value="WMShellUnitTests.apk" />
     </target_preparer>
 
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="framework-base-presubmit" />
-    <option name="test-tag" value="WindowManagerShellTests" />
+    <option name="test-tag" value="WMShellUnitTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.wm.shell.tests" />
         <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
diff --git a/libs/WindowManager/Shell/tests/res/values/config.xml b/libs/WindowManager/Shell/tests/unittest/res/values/config.xml
similarity index 100%
rename from libs/WindowManager/Shell/tests/res/values/config.xml
rename to libs/WindowManager/Shell/tests/unittest/res/values/config.xml
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
new file mode 100644
index 0000000..10672c8
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java
@@ -0,0 +1,121 @@
+/*
+ * 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.wm.shell;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.content.res.Configuration;
+import android.view.SurfaceControl;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+
+/**
+ * Tests for the shell task organizer.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ShellTaskOrganizerTests {
+
+    ShellTaskOrganizer mOrganizer;
+
+    private class TrackingTaskListener implements ShellTaskOrganizer.TaskListener {
+        final ArrayList<RunningTaskInfo> appeared = new ArrayList<>();
+        final ArrayList<RunningTaskInfo> vanished = new ArrayList<>();
+        final ArrayList<RunningTaskInfo> infoChanged = new ArrayList<>();
+
+        @Override
+        public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+            appeared.add(taskInfo);
+        }
+
+        @Override
+        public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+            infoChanged.add(taskInfo);
+        }
+
+        @Override
+        public void onTaskVanished(RunningTaskInfo taskInfo) {
+            vanished.add(taskInfo);
+        }
+
+        @Override
+        public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
+            // Not currently used
+        }
+    }
+
+    @Before
+    public void setUp() {
+        mOrganizer = new ShellTaskOrganizer();
+    }
+
+    @Test
+    public void testAppearedVanished() {
+        RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+        TrackingTaskListener listener = new TrackingTaskListener();
+        mOrganizer.addListener(listener, WINDOWING_MODE_MULTI_WINDOW);
+        mOrganizer.onTaskAppeared(taskInfo, null);
+        assertTrue(listener.appeared.contains(taskInfo));
+
+        mOrganizer.onTaskVanished(taskInfo);
+        assertTrue(listener.vanished.contains(taskInfo));
+    }
+
+    @Test
+    public void testAddListenerExistingTasks() {
+        RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+        mOrganizer.onTaskAppeared(taskInfo, null);
+
+        TrackingTaskListener listener = new TrackingTaskListener();
+        mOrganizer.addListener(listener, WINDOWING_MODE_MULTI_WINDOW);
+        assertTrue(listener.appeared.contains(taskInfo));
+    }
+
+    @Test
+    public void testWindowingModeChange() {
+        RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW);
+        TrackingTaskListener mwListener = new TrackingTaskListener();
+        TrackingTaskListener pipListener = new TrackingTaskListener();
+        mOrganizer.addListener(mwListener, WINDOWING_MODE_MULTI_WINDOW);
+        mOrganizer.addListener(pipListener, WINDOWING_MODE_PINNED);
+        mOrganizer.onTaskAppeared(taskInfo, null);
+        assertTrue(mwListener.appeared.contains(taskInfo));
+        assertTrue(pipListener.appeared.isEmpty());
+
+        taskInfo = createTaskInfo(WINDOWING_MODE_PINNED);
+        mOrganizer.onTaskInfoChanged(taskInfo);
+        assertTrue(mwListener.vanished.contains(taskInfo));
+        assertTrue(pipListener.appeared.contains(taskInfo));
+    }
+
+    private RunningTaskInfo createTaskInfo(int windowingMode) {
+        RunningTaskInfo taskInfo = new RunningTaskInfo();
+        taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
+        return taskInfo;
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/common/DisplayLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
similarity index 100%
rename from libs/WindowManager/Shell/tests/src/com/android/wm/shell/common/DisplayLayoutTest.java
rename to libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 0b13754..90d2537 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -464,6 +464,7 @@
         "RootRenderNode.cpp",
         "shader/Shader.cpp",
         "shader/BitmapShader.cpp",
+        "shader/BlurShader.cpp",
         "shader/ComposeShader.cpp",
         "shader/LinearGradientShader.cpp",
         "shader/RadialGradientShader.cpp",
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 892d43a..473dc53d 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -524,6 +524,7 @@
         // Next greater multiple of SKLITEDL_PAGE.
         fReserved = (fUsed + skip + SKLITEDL_PAGE) & ~(SKLITEDL_PAGE - 1);
         fBytes.realloc(fReserved);
+        LOG_ALWAYS_FATAL_IF(fBytes.get() == nullptr, "realloc(%zd) failed", fReserved);
     }
     SkASSERT(fUsed + skip <= fReserved);
     auto op = (T*)(fBytes.get() + fUsed);
diff --git a/libs/hwui/jni/Picture.cpp b/libs/hwui/jni/Picture.cpp
index d1b9521..8e4203c 100644
--- a/libs/hwui/jni/Picture.cpp
+++ b/libs/hwui/jni/Picture.cpp
@@ -111,7 +111,7 @@
 
     SkPictureRecorder reRecorder;
 
-    SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0);
+    SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight);
     mRecorder->partialReplay(canvas);
     return reRecorder.finishRecordingAsPicture();
 }
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index 9b1972e..7cb7723 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -7,6 +7,7 @@
 #include "include/effects/SkRuntimeEffect.h"
 #include "shader/Shader.h"
 #include "shader/BitmapShader.h"
+#include "shader/BlurShader.h"
 #include "shader/ComposeShader.h"
 #include "shader/LinearGradientShader.h"
 #include "shader/RadialGradientShader.h"
@@ -222,6 +223,22 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////
 
+static jlong BlurShader_create(JNIEnv* env , jobject o, jlong matrixPtr, jfloat sigmaX,
+        jfloat sigmaY, jlong shaderHandle) {
+    auto* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
+    auto* inputShader = reinterpret_cast<Shader*>(shaderHandle);
+
+    auto* blurShader = new BlurShader(
+                sigmaX,
+                sigmaY,
+                inputShader,
+                matrix
+            );
+    return reinterpret_cast<jlong>(blurShader);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
 static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderFactory, jlong matrixPtr,
         jbyteArray inputs, jlong colorSpaceHandle, jboolean isOpaque) {
     auto* effect = reinterpret_cast<SkRuntimeEffect*>(shaderFactory);
@@ -273,6 +290,10 @@
     { "nativeCreate",      "(JJII)J",  (void*)BitmapShader_constructor },
 };
 
+static const JNINativeMethod gBlurShaderMethods[] = {
+    { "nativeCreate",      "(JFFJ)J", (void*)BlurShader_create }
+};
+
 static const JNINativeMethod gLinearGradientMethods[] = {
     { "nativeCreate",     "(JFFFF[J[FIJ)J",  (void*)LinearGradient_create     },
 };
@@ -304,6 +325,8 @@
                                   NELEM(gShaderMethods));
     android::RegisterMethodsOrDie(env, "android/graphics/BitmapShader", gBitmapShaderMethods,
                                   NELEM(gBitmapShaderMethods));
+    android::RegisterMethodsOrDie(env, "android/graphics/BlurShader", gBlurShaderMethods,
+                                  NELEM(gBlurShaderMethods));
     android::RegisterMethodsOrDie(env, "android/graphics/LinearGradient", gLinearGradientMethods,
                                   NELEM(gLinearGradientMethods));
     android::RegisterMethodsOrDie(env, "android/graphics/RadialGradient", gRadialGradientMethods,
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 0c5cf682..76ec078 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -361,20 +361,6 @@
     mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
     mGetDeviceQueue(mDevice, mGraphicsQueueIndex, 1, &mAHBUploadQueue);
 
-    // create the command pool for the command buffers
-    if (VK_NULL_HANDLE == mCommandPool) {
-        VkCommandPoolCreateInfo commandPoolInfo;
-        memset(&commandPoolInfo, 0, sizeof(VkCommandPoolCreateInfo));
-        commandPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
-        // this needs to be on the render queue
-        commandPoolInfo.queueFamilyIndex = mGraphicsQueueIndex;
-        commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
-        SkDEBUGCODE(VkResult res =)
-                mCreateCommandPool(mDevice, &commandPoolInfo, nullptr, &mCommandPool);
-        SkASSERT(VK_SUCCESS == res);
-    }
-    LOG_ALWAYS_FATAL_IF(mCommandPool == VK_NULL_HANDLE);
-
     mGetDeviceQueue(mDevice, mPresentQueueIndex, 0, &mPresentQueue);
 
     if (Properties::enablePartialUpdates && Properties::useBufferAge) {
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 13335f3..75c05b8 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -165,7 +165,6 @@
     VkQueue mAHBUploadQueue = VK_NULL_HANDLE;
     uint32_t mPresentQueueIndex;
     VkQueue mPresentQueue = VK_NULL_HANDLE;
-    VkCommandPool mCommandPool = VK_NULL_HANDLE;
 
     // Variables saved to populate VkFunctorInitParams.
     static const uint32_t mAPIVersion = VK_MAKE_VERSION(1, 1, 0);
diff --git a/libs/hwui/shader/BlurShader.cpp b/libs/hwui/shader/BlurShader.cpp
new file mode 100644
index 0000000..4d18cdd
--- /dev/null
+++ b/libs/hwui/shader/BlurShader.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "BlurShader.h"
+#include "SkImageFilters.h"
+#include "SkRefCnt.h"
+#include "utils/Blur.h"
+
+namespace android::uirenderer {
+BlurShader::BlurShader(float radiusX, float radiusY, Shader* inputShader, const SkMatrix* matrix)
+    : Shader(matrix)
+    , skImageFilter(
+            SkImageFilters::Blur(
+                    Blur::convertRadiusToSigma(radiusX),
+                    Blur::convertRadiusToSigma(radiusY),
+                    inputShader ? inputShader->asSkImageFilter() : nullptr)
+            ) { }
+
+sk_sp<SkImageFilter> BlurShader::makeSkImageFilter() {
+    return skImageFilter;
+}
+
+BlurShader::~BlurShader() {}
+
+} // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/shader/BlurShader.h b/libs/hwui/shader/BlurShader.h
new file mode 100644
index 0000000..9eb22bd
--- /dev/null
+++ b/libs/hwui/shader/BlurShader.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma once
+#include "Shader.h"
+
+namespace android::uirenderer {
+
+/**
+ * Shader implementation that blurs another Shader instance or the source bitmap
+ */
+class BlurShader : public Shader {
+public:
+    /**
+     * Creates a BlurShader instance with the provided radius values to blur along the x and y
+     * axis accordingly.
+     *
+     * This will blur the contents of the provided input shader if it is non-null, otherwise
+     * the source bitmap will be blurred instead.
+     */
+    BlurShader(float radiusX, float radiusY, Shader* inputShader, const SkMatrix* matrix);
+    ~BlurShader() override;
+protected:
+    sk_sp<SkImageFilter> makeSkImageFilter() override;
+private:
+    sk_sp<SkImageFilter> skImageFilter;
+};
+
+} // namespace android::uirenderer
\ No newline at end of file
diff --git a/libs/hwui/shader/Shader.h b/libs/hwui/shader/Shader.h
index 3c0cdaa..6403e11 100644
--- a/libs/hwui/shader/Shader.h
+++ b/libs/hwui/shader/Shader.h
@@ -32,7 +32,9 @@
 class Shader: public SkRefCnt {
 public:
     /**
-     * Creates a Shader instance with an optional transformation matrix
+     * Creates a Shader instance with an optional transformation matrix. The transformation matrix
+     * is copied internally and ownership is unchanged. It is the responsibility of the caller to
+     * deallocate it appropriately.
      * @param matrix Optional matrix to transform the underlying SkShader or SkImageFilter
      */
     Shader(const SkMatrix* matrix);
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index fcc64fd..f77ca2a 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -73,7 +73,7 @@
 
     // Test picture recording.
     SkPictureRecorder recorder;
-    SkCanvas* skPicCanvas = recorder.beginRecording(1, 1, NULL, 0);
+    SkCanvas* skPicCanvas = recorder.beginRecording(1, 1);
     SkiaCanvas picCanvas(skPicCanvas);
     picCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
     sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
@@ -104,7 +104,7 @@
 
     // Create a picture canvas.
     SkPictureRecorder recorder;
-    SkCanvas* skPicCanvas = recorder.beginRecording(1, 1, NULL, 0);
+    SkCanvas* skPicCanvas = recorder.beginRecording(1, 1);
     SkiaCanvas picCanvas(skPicCanvas);
     state = picCanvas.captureCanvasState();
 
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index 80b555b..45da008 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -168,7 +168,7 @@
         updatePointerLocked();
     } else {
         mLocked.pointerFadeDirection = -1;
-        mContext.startAnimation();
+        startAnimationLocked();
     }
 }
 
@@ -185,7 +185,7 @@
         updatePointerLocked();
     } else {
         mLocked.pointerFadeDirection = 1;
-        mContext.startAnimation();
+        startAnimationLocked();
     }
 }
 
@@ -312,10 +312,9 @@
     updatePointerLocked();
 }
 
-bool MouseCursorController::doFadingAnimation(nsecs_t timestamp, bool keepAnimating) {
+bool MouseCursorController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
     nsecs_t frameDelay = timestamp - mContext.getAnimationTime();
-
-    std::scoped_lock lock(mLock);
+    bool keepAnimating = false;
 
     // Animate pointer fade.
     if (mLocked.pointerFadeDirection < 0) {
@@ -337,13 +336,10 @@
         }
         updatePointerLocked();
     }
-
     return keepAnimating;
 }
 
-bool MouseCursorController::doBitmapAnimation(nsecs_t timestamp) {
-    std::scoped_lock lock(mLock);
-
+bool MouseCursorController::doBitmapAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
     std::map<int32_t, PointerAnimation>::const_iterator iter =
             mLocked.animationResources.find(mLocked.requestedPointerType);
     if (iter == mLocked.animationResources.end()) {
@@ -364,7 +360,6 @@
 
         spriteController->closeTransaction();
     }
-
     // Keep animating.
     return true;
 }
@@ -399,7 +394,7 @@
                 if (anim_iter != mLocked.animationResources.end()) {
                     mLocked.animationFrameIndex = 0;
                     mLocked.lastFrameUpdatedTime = systemTime(SYSTEM_TIME_MONOTONIC);
-                    mContext.startAnimation();
+                    startAnimationLocked();
                 }
                 mLocked.pointerSprite->setIcon(iter->second);
             } else {
@@ -457,4 +452,38 @@
     return mLocked.resourcesLoaded;
 }
 
+bool MouseCursorController::doAnimations(nsecs_t timestamp) {
+    std::scoped_lock lock(mLock);
+    bool keepFading = doFadingAnimationLocked(timestamp);
+    bool keepBitmap = doBitmapAnimationLocked(timestamp);
+    bool keepAnimating = keepFading || keepBitmap;
+    if (!keepAnimating) {
+        /*
+         * We know that this callback will be removed before another
+         * is added. mLock in PointerAnimator will not be released
+         * until after this is removed, and adding another callback
+         * requires that lock. Thus it's safe to set mLocked.animating
+         * here.
+         */
+        mLocked.animating = false;
+    }
+    return keepAnimating;
+}
+
+void MouseCursorController::startAnimationLocked() REQUIRES(mLock) {
+    using namespace std::placeholders;
+
+    if (mLocked.animating) {
+        return;
+    }
+    mLocked.animating = true;
+
+    std::function<bool(nsecs_t)> func = std::bind(&MouseCursorController::doAnimations, this, _1);
+    /*
+     * Using -1 for displayId here to avoid removing the callback
+     * if a TouchSpotController with the same display is removed.
+     */
+    mContext.addAnimationCallback(-1, func);
+}
+
 } // namespace android
diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h
index 448165b..e6dfc4c 100644
--- a/libs/input/MouseCursorController.h
+++ b/libs/input/MouseCursorController.h
@@ -25,6 +25,7 @@
 #include <utils/Looper.h>
 #include <utils/RefBase.h>
 
+#include <functional>
 #include <map>
 #include <memory>
 #include <vector>
@@ -61,8 +62,7 @@
     void getAdditionalMouseResources();
     bool isViewportValid();
 
-    bool doBitmapAnimation(nsecs_t timestamp);
-    bool doFadingAnimation(nsecs_t timestamp, bool keepAnimating);
+    bool doAnimations(nsecs_t timestamp);
 
     bool resourcesLoaded();
 
@@ -96,6 +96,8 @@
 
         int32_t buttonState;
 
+        bool animating{false};
+
     } mLocked GUARDED_BY(mLock);
 
     bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
@@ -104,6 +106,11 @@
     void updatePointerLocked();
 
     void loadResourcesLocked(bool getAdditionalMouseResources);
+
+    bool doBitmapAnimationLocked(nsecs_t timestamp);
+    bool doFadingAnimationLocked(nsecs_t timestamp);
+
+    void startAnimationLocked();
 };
 
 } // namespace android
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index 14c96ce..8f04cfb 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -57,7 +57,6 @@
 
     controller->mContext.setHandlerController(controller);
     controller->mContext.setCallbackController(controller);
-    controller->mContext.initializeDisplayEventReceiver();
     return controller;
 }
 
@@ -189,24 +188,6 @@
     mCursorController.setCustomPointerIcon(icon);
 }
 
-void PointerController::doAnimate(nsecs_t timestamp) {
-    std::scoped_lock lock(mLock);
-
-    mContext.setAnimationPending(false);
-
-    bool keepFading = false;
-    keepFading = mCursorController.doFadingAnimation(timestamp, keepFading);
-
-    for (auto& [displayID, spotController] : mLocked.spotControllers) {
-        keepFading = spotController.doFadingAnimation(timestamp, keepFading);
-    }
-
-    bool keepBitmapFlipping = mCursorController.doBitmapAnimation(timestamp);
-    if (keepFading || keepBitmapFlipping) {
-        mContext.startAnimation();
-    }
-}
-
 void PointerController::doInactivityTimeout() {
     fade(Transition::GRADUAL);
 }
@@ -221,6 +202,11 @@
     for (auto it = mLocked.spotControllers.begin(); it != mLocked.spotControllers.end();) {
         int32_t displayID = it->first;
         if (!displayIdSet.count(displayID)) {
+            /*
+             * Ensures that an in-progress animation won't dereference
+             * a null pointer to TouchSpotController.
+             */
+            mContext.removeAnimationCallback(displayID);
             it = mLocked.spotControllers.erase(it);
         } else {
             ++it;
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index 1f561da..827fcf1 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -70,7 +70,6 @@
     void setCustomPointerIcon(const SpriteIcon& icon);
     void setInactivityTimeout(InactivityTimeout inactivityTimeout);
     void doInactivityTimeout();
-    void doAnimate(nsecs_t timestamp);
     void reloadPointerResources();
     void onDisplayViewportsUpdated(std::vector<DisplayViewport>& viewports);
 
diff --git a/libs/input/PointerControllerContext.cpp b/libs/input/PointerControllerContext.cpp
index 2d7e22b..f30e8d8 100644
--- a/libs/input/PointerControllerContext.cpp
+++ b/libs/input/PointerControllerContext.cpp
@@ -38,10 +38,10 @@
         mSpriteController(spriteController),
         mHandler(new MessageHandler()),
         mCallback(new LooperCallback()),
-        mController(controller) {
+        mController(controller),
+        mAnimator(*this) {
     std::scoped_lock lock(mLock);
     mLocked.inactivityTimeout = InactivityTimeout::NORMAL;
-    mLocked.animationPending = false;
 }
 
 PointerControllerContext::~PointerControllerContext() {
@@ -57,15 +57,6 @@
     }
 }
 
-void PointerControllerContext::startAnimation() {
-    std::scoped_lock lock(mLock);
-    if (!mLocked.animationPending) {
-        mLocked.animationPending = true;
-        mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC);
-        mDisplayEventReceiver.requestNextVsync();
-    }
-}
-
 void PointerControllerContext::resetInactivityTimeout() {
     std::scoped_lock lock(mLock);
     resetInactivityTimeoutLocked();
@@ -85,14 +76,8 @@
     mLooper->removeMessages(mHandler, MessageHandler::MSG_INACTIVITY_TIMEOUT);
 }
 
-void PointerControllerContext::setAnimationPending(bool animationPending) {
-    std::scoped_lock lock(mLock);
-    mLocked.animationPending = animationPending;
-}
-
-nsecs_t PointerControllerContext::getAnimationTime() {
-    std::scoped_lock lock(mLock);
-    return mLocked.animationTime;
+nsecs_t PointerControllerContext::getAnimationTime() REQUIRES(mAnimator.mLock) {
+    return mAnimator.getAnimationTimeLocked();
 }
 
 void PointerControllerContext::setHandlerController(std::shared_ptr<PointerController> controller) {
@@ -112,31 +97,8 @@
     return mSpriteController;
 }
 
-void PointerControllerContext::initializeDisplayEventReceiver() {
-    if (mDisplayEventReceiver.initCheck() == NO_ERROR) {
-        mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, Looper::EVENT_INPUT,
-                       mCallback, nullptr);
-    } else {
-        ALOGE("Failed to initialize DisplayEventReceiver.");
-    }
-}
-
 void PointerControllerContext::handleDisplayEvents() {
-    bool gotVsync = false;
-    ssize_t n;
-    nsecs_t timestamp;
-    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
-    while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
-        for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
-            if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
-                timestamp = buf[i].header.timestamp;
-                gotVsync = true;
-            }
-        }
-    }
-    if (gotVsync) {
-        mController.doAnimate(timestamp);
-    }
+    mAnimator.handleVsyncEvents();
 }
 
 void PointerControllerContext::MessageHandler::handleMessage(const Message& message) {
@@ -176,4 +138,91 @@
     return 1; // keep the callback
 }
 
+void PointerControllerContext::addAnimationCallback(int32_t displayId,
+                                                    std::function<bool(nsecs_t)> callback) {
+    mAnimator.addCallback(displayId, callback);
+}
+
+void PointerControllerContext::removeAnimationCallback(int32_t displayId) {
+    mAnimator.removeCallback(displayId);
+}
+
+PointerControllerContext::PointerAnimator::PointerAnimator(PointerControllerContext& context)
+      : mContext(context) {
+    initializeDisplayEventReceiver();
+}
+
+void PointerControllerContext::PointerAnimator::initializeDisplayEventReceiver() {
+    if (mDisplayEventReceiver.initCheck() == NO_ERROR) {
+        mContext.mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK,
+                                Looper::EVENT_INPUT, mContext.mCallback, nullptr);
+    } else {
+        ALOGE("Failed to initialize DisplayEventReceiver.");
+    }
+}
+
+void PointerControllerContext::PointerAnimator::addCallback(int32_t displayId,
+                                                            std::function<bool(nsecs_t)> callback) {
+    std::scoped_lock lock(mLock);
+    mLocked.callbacks[displayId] = callback;
+    startAnimationLocked();
+}
+
+void PointerControllerContext::PointerAnimator::removeCallback(int32_t displayId) {
+    std::scoped_lock lock(mLock);
+    auto it = mLocked.callbacks.find(displayId);
+    if (it == mLocked.callbacks.end()) {
+        return;
+    }
+    mLocked.callbacks.erase(it);
+}
+
+void PointerControllerContext::PointerAnimator::handleVsyncEvents() {
+    bool gotVsync = false;
+    ssize_t n;
+    nsecs_t timestamp;
+    DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+    while ((n = mDisplayEventReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+        for (size_t i = 0; i < static_cast<size_t>(n); ++i) {
+            if (buf[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
+                timestamp = buf[i].header.timestamp;
+                gotVsync = true;
+            }
+        }
+    }
+    if (gotVsync) {
+        std::scoped_lock lock(mLock);
+        mLocked.animationPending = false;
+        handleCallbacksLocked(timestamp);
+    }
+}
+
+nsecs_t PointerControllerContext::PointerAnimator::getAnimationTimeLocked() REQUIRES(mLock) {
+    return mLocked.animationTime;
+}
+
+void PointerControllerContext::PointerAnimator::startAnimationLocked() REQUIRES(mLock) {
+    if (!mLocked.animationPending) {
+        mLocked.animationPending = true;
+        mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC);
+        mDisplayEventReceiver.requestNextVsync();
+    }
+}
+
+void PointerControllerContext::PointerAnimator::handleCallbacksLocked(nsecs_t timestamp)
+        REQUIRES(mLock) {
+    for (auto it = mLocked.callbacks.begin(); it != mLocked.callbacks.end();) {
+        bool keepCallback = it->second(timestamp);
+        if (!keepCallback) {
+            it = mLocked.callbacks.erase(it);
+        } else {
+            ++it;
+        }
+    }
+
+    if (!mLocked.callbacks.empty()) {
+        startAnimationLocked();
+    }
+}
+
 } // namespace android
diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h
index 92e1bda..98073fe 100644
--- a/libs/input/PointerControllerContext.h
+++ b/libs/input/PointerControllerContext.h
@@ -26,6 +26,7 @@
 #include <utils/Looper.h>
 #include <utils/RefBase.h>
 
+#include <functional>
 #include <map>
 #include <memory>
 #include <vector>
@@ -35,6 +36,8 @@
 namespace android {
 
 class PointerController;
+class MouseCursorController;
+class TouchSpotController;
 
 /*
  * Pointer resources.
@@ -96,7 +99,6 @@
     void startAnimation();
     void setInactivityTimeout(InactivityTimeout inactivityTimeout);
 
-    void setAnimationPending(bool animationPending);
     nsecs_t getAnimationTime();
 
     void clearSpotsByDisplay(int32_t displayId);
@@ -107,9 +109,11 @@
     sp<PointerControllerPolicyInterface> getPolicy();
     sp<SpriteController> getSpriteController();
 
-    void initializeDisplayEventReceiver();
     void handleDisplayEvents();
 
+    void addAnimationCallback(int32_t displayId, std::function<bool(nsecs_t)> callback);
+    void removeAnimationCallback(int32_t displayId);
+
     class MessageHandler : public virtual android::MessageHandler {
     public:
         enum {
@@ -127,22 +131,47 @@
     };
 
 private:
+    class PointerAnimator {
+    public:
+        PointerAnimator(PointerControllerContext& context);
+
+        void addCallback(int32_t displayId, std::function<bool(nsecs_t)> callback);
+        void removeCallback(int32_t displayId);
+        void handleVsyncEvents();
+        nsecs_t getAnimationTimeLocked();
+
+        mutable std::mutex mLock;
+
+    private:
+        struct Locked {
+            bool animationPending{false};
+            nsecs_t animationTime{systemTime(SYSTEM_TIME_MONOTONIC)};
+
+            std::unordered_map<int32_t, std::function<bool(nsecs_t)>> callbacks;
+        } mLocked GUARDED_BY(mLock);
+
+        DisplayEventReceiver mDisplayEventReceiver;
+
+        PointerControllerContext& mContext;
+
+        void initializeDisplayEventReceiver();
+        void startAnimationLocked();
+        void handleCallbacksLocked(nsecs_t timestamp);
+    };
+
     sp<PointerControllerPolicyInterface> mPolicy;
     sp<Looper> mLooper;
     sp<SpriteController> mSpriteController;
     sp<MessageHandler> mHandler;
     sp<LooperCallback> mCallback;
 
-    DisplayEventReceiver mDisplayEventReceiver;
-
     PointerController& mController;
 
+    PointerAnimator mAnimator;
+
     mutable std::mutex mLock;
 
     struct Locked {
-        bool animationPending;
-        nsecs_t animationTime;
-
         InactivityTimeout inactivityTimeout;
     } mLocked GUARDED_BY(mLock);
 
diff --git a/libs/input/TouchSpotController.cpp b/libs/input/TouchSpotController.cpp
index c7430ce..f7c685f 100644
--- a/libs/input/TouchSpotController.cpp
+++ b/libs/input/TouchSpotController.cpp
@@ -142,7 +142,8 @@
 }
 
 TouchSpotController::Spot* TouchSpotController::createAndAddSpotLocked(uint32_t id,
-                                                                       std::vector<Spot*>& spots) {
+                                                                       std::vector<Spot*>& spots)
+        REQUIRES(mLock) {
     // Remove spots until we have fewer than MAX_SPOTS remaining.
     while (spots.size() >= MAX_SPOTS) {
         Spot* spot = removeFirstFadingSpotLocked(spots);
@@ -186,14 +187,13 @@
     if (mLocked.recycledSprites.size() < MAX_RECYCLED_SPRITES) {
         mLocked.recycledSprites.push_back(spot->sprite);
     }
-
     delete spot;
 }
 
 void TouchSpotController::fadeOutAndReleaseSpotLocked(Spot* spot) REQUIRES(mLock) {
     if (spot->id != Spot::INVALID_ID) {
         spot->id = Spot::INVALID_ID;
-        mContext.startAnimation();
+        startAnimationLocked();
     }
 }
 
@@ -209,8 +209,24 @@
     mContext.getPolicy()->loadPointerResources(&mResources, mDisplayId);
 }
 
-bool TouchSpotController::doFadingAnimation(nsecs_t timestamp, bool keepAnimating) {
+bool TouchSpotController::doAnimations(nsecs_t timestamp) {
     std::scoped_lock lock(mLock);
+    bool keepAnimating = doFadingAnimationLocked(timestamp);
+    if (!keepAnimating) {
+        /*
+         * We know that this callback will be removed before another
+         * is added. mLock in PointerAnimator will not be released
+         * until after this is removed, and adding another callback
+         * requires that lock. Thus it's safe to set mLocked.animating
+         * here.
+         */
+        mLocked.animating = false;
+    }
+    return keepAnimating;
+}
+
+bool TouchSpotController::doFadingAnimationLocked(nsecs_t timestamp) REQUIRES(mLock) {
+    bool keepAnimating = false;
     nsecs_t animationTime = mContext.getAnimationTime();
     nsecs_t frameDelay = timestamp - animationTime;
     size_t numSpots = mLocked.displaySpots.size();
@@ -233,4 +249,16 @@
     return keepAnimating;
 }
 
+void TouchSpotController::startAnimationLocked() REQUIRES(mLock) {
+    using namespace std::placeholders;
+
+    if (mLocked.animating) {
+        return;
+    }
+    mLocked.animating = true;
+
+    std::function<bool(nsecs_t)> func = std::bind(&TouchSpotController::doAnimations, this, _1);
+    mContext.addAnimationCallback(mDisplayId, func);
+}
+
 } // namespace android
diff --git a/libs/input/TouchSpotController.h b/libs/input/TouchSpotController.h
index f3b3550..703de36 100644
--- a/libs/input/TouchSpotController.h
+++ b/libs/input/TouchSpotController.h
@@ -17,6 +17,8 @@
 #ifndef _UI_TOUCH_SPOT_CONTROLLER_H
 #define _UI_TOUCH_SPOT_CONTROLLER_H
 
+#include <functional>
+
 #include "PointerControllerContext.h"
 
 namespace android {
@@ -34,7 +36,7 @@
     void clearSpots();
 
     void reloadSpotResources();
-    bool doFadingAnimation(nsecs_t timestamp, bool keepAnimating);
+    bool doAnimations(nsecs_t timestamp);
 
 private:
     struct Spot {
@@ -76,6 +78,8 @@
         std::vector<Spot*> displaySpots;
         std::vector<sp<Sprite>> recycledSprites;
 
+        bool animating{false};
+
     } mLocked GUARDED_BY(mLock);
 
     Spot* getSpot(uint32_t id, const std::vector<Spot*>& spots);
@@ -84,6 +88,8 @@
     void releaseSpotLocked(Spot* spot);
     void fadeOutAndReleaseSpotLocked(Spot* spot);
     void fadeOutAndReleaseAllSpotsLocked();
+    bool doFadingAnimationLocked(nsecs_t timestamp);
+    void startAnimationLocked();
 };
 
 } // namespace android
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 1803027..6fc702e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -29,6 +29,8 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -215,6 +217,7 @@
      * @see #EXTRA_PROVIDER_ENABLED
      * @see #isProviderEnabled(String)
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String PROVIDERS_CHANGED_ACTION = "android.location.PROVIDERS_CHANGED";
 
     /**
@@ -243,6 +246,7 @@
      * @see #EXTRA_LOCATION_ENABLED
      * @see #isLocationEnabled()
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
 
     /**
diff --git a/location/java/android/location/timezone/LocationTimeZoneEvent.java b/location/java/android/location/timezone/LocationTimeZoneEvent.java
index ea3353c..540bdff 100644
--- a/location/java/android/location/timezone/LocationTimeZoneEvent.java
+++ b/location/java/android/location/timezone/LocationTimeZoneEvent.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -37,7 +38,7 @@
     @IntDef({ EVENT_TYPE_UNKNOWN, EVENT_TYPE_SUCCESS, EVENT_TYPE_SUCCESS })
     @interface EventType {}
 
-    /** Uninitialized value for {@link #mEventType} */
+    /** Uninitialized value for {@link #mEventType} - must not be used for real events. */
     private static final int EVENT_TYPE_UNKNOWN = 0;
 
     /**
@@ -60,6 +61,9 @@
 
     private static final int EVENT_TYPE_MAX = EVENT_TYPE_UNCERTAIN;
 
+    @NonNull
+    private final UserHandle mUserHandle;
+
     @EventType
     private final int mEventType;
 
@@ -68,8 +72,9 @@
 
     private final long mElapsedRealtimeNanos;
 
-    private LocationTimeZoneEvent(@EventType int eventType, @NonNull List<String> timeZoneIds,
-            long elapsedRealtimeNanos) {
+    private LocationTimeZoneEvent(@NonNull UserHandle userHandle, @EventType int eventType,
+            @NonNull List<String> timeZoneIds, long elapsedRealtimeNanos) {
+        mUserHandle = Objects.requireNonNull(userHandle);
         mEventType = checkValidEventType(eventType);
         mTimeZoneIds = immutableList(timeZoneIds);
 
@@ -83,6 +88,14 @@
     }
 
     /**
+     * Returns the current user when the event was generated.
+     */
+    @NonNull
+    public UserHandle getUserHandle() {
+        return mUserHandle;
+    }
+
+    /**
      * Returns the time of this fix, in elapsed real-time since system boot.
      *
      * <p>This value can be reliably compared to {@link
@@ -117,7 +130,8 @@
     @Override
     public String toString() {
         return "LocationTimeZoneEvent{"
-                + "mEventType=" + mEventType
+                + "mUserHandle=" + mUserHandle
+                + ", mEventType=" + mEventType
                 + ", mTimeZoneIds=" + mTimeZoneIds
                 + ", mElapsedRealtimeNanos=" + mElapsedRealtimeNanos
                 + '}';
@@ -127,12 +141,14 @@
             new Parcelable.Creator<LocationTimeZoneEvent>() {
                 @Override
                 public LocationTimeZoneEvent createFromParcel(Parcel in) {
+                    UserHandle userHandle = UserHandle.readFromParcel(in);
                     int eventType = in.readInt();
                     @SuppressWarnings("unchecked")
                     ArrayList<String> timeZoneIds =
                             (ArrayList<String>) in.readArrayList(null /* classLoader */);
                     long elapsedRealtimeNanos = in.readLong();
-                    return new LocationTimeZoneEvent(eventType, timeZoneIds, elapsedRealtimeNanos);
+                    return new LocationTimeZoneEvent(
+                            userHandle, eventType, timeZoneIds, elapsedRealtimeNanos);
                 }
 
                 @Override
@@ -148,6 +164,7 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
+        mUserHandle.writeToParcel(parcel, flags);
         parcel.writeInt(mEventType);
         parcel.writeList(mTimeZoneIds);
         parcel.writeLong(mElapsedRealtimeNanos);
@@ -162,19 +179,21 @@
             return false;
         }
         LocationTimeZoneEvent that = (LocationTimeZoneEvent) o;
-        return mEventType == that.mEventType
+        return mUserHandle.equals(that.mUserHandle)
+                && mEventType == that.mEventType
                 && mElapsedRealtimeNanos == that.mElapsedRealtimeNanos
                 && mTimeZoneIds.equals(that.mTimeZoneIds);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mEventType, mTimeZoneIds, mElapsedRealtimeNanos);
+        return Objects.hash(mUserHandle, mEventType, mTimeZoneIds, mElapsedRealtimeNanos);
     }
 
     /** @hide */
     public static final class Builder {
 
+        private UserHandle mUserHandle;
         private @EventType int mEventType = EVENT_TYPE_UNKNOWN;
         private @NonNull List<String> mTimeZoneIds = Collections.emptyList();
         private long mElapsedRealtimeNanos;
@@ -186,13 +205,22 @@
          * Sets the contents of this from the supplied instance.
          */
         public Builder(@NonNull LocationTimeZoneEvent ltz) {
+            mUserHandle = ltz.mUserHandle;
             mEventType = ltz.mEventType;
             mTimeZoneIds = ltz.mTimeZoneIds;
             mElapsedRealtimeNanos = ltz.mElapsedRealtimeNanos;
         }
 
         /**
-         * Set the time zone ID of this fix.
+         * Set the current user when this event was generated.
+         */
+        public Builder setUserHandle(@NonNull UserHandle userHandle) {
+            mUserHandle = Objects.requireNonNull(userHandle);
+            return this;
+        }
+
+        /**
+         * Set the time zone ID of this event.
          */
         public Builder setEventType(@EventType int eventType) {
             checkValidEventType(eventType);
@@ -201,7 +229,7 @@
         }
 
         /**
-         * Sets the time zone IDs of this fix.
+         * Sets the time zone IDs of this event.
          */
         public Builder setTimeZoneIds(@NonNull List<String> timeZoneIds) {
             mTimeZoneIds = Objects.requireNonNull(timeZoneIds);
@@ -209,9 +237,7 @@
         }
 
         /**
-         * Sets the time of this fix, in elapsed real-time since system boot.
-         *
-         * @param time elapsed real-time of fix, in nanoseconds since system boot.
+         * Sets the time of this event, in elapsed real-time since system boot.
          */
         public Builder setElapsedRealtimeNanos(long time) {
             mElapsedRealtimeNanos = time;
@@ -222,8 +248,8 @@
          * Builds a {@link LocationTimeZoneEvent} instance.
          */
         public LocationTimeZoneEvent build() {
-            return new LocationTimeZoneEvent(this.mEventType, this.mTimeZoneIds,
-                    this.mElapsedRealtimeNanos);
+            return new LocationTimeZoneEvent(
+                    mUserHandle, mEventType, mTimeZoneIds, mElapsedRealtimeNanos);
         }
     }
 
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
index 0143c88..c533c20 100644
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
+++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
@@ -32,7 +32,7 @@
 /**
  * Base class for location time zone providers implemented as unbundled services.
  *
- * TODO Provide details of the expected service actions.
+ * TODO(b/152744911): Provide details of the expected service actions and threading.
  *
  * <p>IMPORTANT: This class is effectively a public API for unbundled applications, and must remain
  * API stable.
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
index dd80466..e898bbf 100644
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
@@ -44,6 +44,24 @@
     }
 
     @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        LocationTimeZoneProviderRequestUnbundled that =
+                (LocationTimeZoneProviderRequestUnbundled) o;
+        return mRequest.equals(that.mRequest);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRequest);
+    }
+
+    @Override
     public String toString() {
         return mRequest.toString();
     }
diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java
index e1bff03..4e2ae5c 100644
--- a/media/java/android/media/MediaTranscodeManager.java
+++ b/media/java/android/media/MediaTranscodeManager.java
@@ -36,7 +36,10 @@
 import java.io.FileNotFoundException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
@@ -94,11 +97,17 @@
  TODO(hkuang): Clarify whether supports framerate conversion.
  @hide
  */
-public final class MediaTranscodeManager {
+public final class MediaTranscodeManager implements AutoCloseable {
     private static final String TAG = "MediaTranscodeManager";
 
     private static final String MEDIA_TRANSCODING_SERVICE = "media.transcoding";
 
+    /** Maximum number of retry to connect to the service. */
+    private static final int CONNECT_SERVICE_RETRY_COUNT = 100;
+
+    /** Interval between trying to reconnect to the service. */
+    private static final int INTERVAL_CONNECT_SERVICE_RETRY_MS = 40;
+
     /**
      * Default transcoding type.
      * @hide
@@ -117,6 +126,25 @@
      */
     public static final int TRANSCODING_TYPE_IMAGE = 2;
 
+    @Override
+    public void close() throws Exception {
+        release();
+    }
+
+    /**
+     * Releases the MediaTranscodeManager.
+     */
+    //TODO(hkuang): add test for it.
+    private void release() throws Exception {
+        synchronized (mLock) {
+            if (mTranscodingClient != null) {
+                mTranscodingClient.unregister();
+            } else {
+                throw new UnsupportedOperationException("Failed to release");
+            }
+        }
+    }
+
     /** @hide */
     @IntDef(prefix = {"TRANSCODING_TYPE_"}, value = {
             TRANSCODING_TYPE_UNKNOWN,
@@ -181,10 +209,12 @@
     private final String mPackageName;
     private final int mPid;
     private final int mUid;
-    private final ExecutorService mCallbackExecutor = Executors.newSingleThreadExecutor();
-    private static MediaTranscodeManager sMediaTranscodeManager;
+    private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
     private final HashMap<Integer, TranscodingJob> mPendingTranscodingJobs = new HashMap();
-    @NonNull private ITranscodingClient mTranscodingClient;
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    @NonNull private ITranscodingClient mTranscodingClient = null;
+    private static MediaTranscodeManager sMediaTranscodeManager;
 
     private void handleTranscodingFinished(int jobId, TranscodingResultParcel result) {
         synchronized (mPendingTranscodingJobs) {
@@ -209,7 +239,7 @@
         }
     }
 
-    private void handleTranscodingFailed(int jobId, int errorCodec) {
+    private void handleTranscodingFailed(int jobId, int errorCode) {
         synchronized (mPendingTranscodingJobs) {
             // Gets the job associated with the jobId and removes it from
             // mPendingTranscodingJobs.
@@ -254,6 +284,98 @@
         }
     }
 
+    private static IMediaTranscodingService getService(boolean retry) {
+        int retryCount = !retry ? 1 :  CONNECT_SERVICE_RETRY_COUNT;
+        Log.i(TAG, "get service with rety " + retryCount);
+        for (int count = 1;  count <= retryCount; count++) {
+            Log.d(TAG, "Trying to connect to service. Try count: " + count);
+            IMediaTranscodingService service = IMediaTranscodingService.Stub.asInterface(
+                    ServiceManager.getService(MEDIA_TRANSCODING_SERVICE));
+            if (service != null) {
+                return service;
+            }
+            try {
+                // Sleep a bit before retry.
+                Thread.sleep(INTERVAL_CONNECT_SERVICE_RETRY_MS);
+            } catch (InterruptedException ie) {
+                /* ignore */
+            }
+        }
+
+        throw new UnsupportedOperationException("Failed to connect to MediaTranscoding service");
+    }
+
+    /*
+     * Handle client binder died event.
+     * Upon receiving a binder died event of the client, we will do the following:
+     * 1) For the job that is running, notify the client that the job is failed with error code,
+     *    so client could choose to retry the job or not.
+     *    TODO(hkuang): Add a new error code to signal service died error.
+     * 2) For the jobs that is still pending or paused, we will resubmit the job internally once
+     *    we successfully reconnect to the service and register a new client.
+     * 3) When trying to connect to the service and register a new client. The service may need time
+     *    to reboot or never boot up again. So we will retry for a number of times. If we still
+     *    could not connect, we will notify client job failure for the pending and paused jobs.
+     */
+    private void onClientDied() {
+        synchronized (mLock) {
+            mTranscodingClient = null;
+        }
+
+        // Delegates the job notification and retry to the executor as it may take some time.
+        mExecutor.execute(() -> {
+            // List to track the jobs that we want to retry.
+            List<TranscodingJob> retryJobs = new ArrayList<TranscodingJob>();
+
+            // First notify the client of job failure for all the running jobs.
+            synchronized (mPendingTranscodingJobs) {
+                for (Map.Entry<Integer, TranscodingJob> entry :
+                        mPendingTranscodingJobs.entrySet()) {
+                    TranscodingJob job = entry.getValue();
+
+                    if (job.getStatus() == TranscodingJob.STATUS_RUNNING) {
+                        job.updateStatusAndResult(TranscodingJob.STATUS_FINISHED,
+                                TranscodingJob.RESULT_ERROR);
+
+                        // Remove the job from pending jobs.
+                        mPendingTranscodingJobs.remove(entry.getKey());
+
+                        if (job.mListener != null && job.mListenerExecutor != null) {
+                            Log.i(TAG, "Notify client job failed");
+                            job.mListenerExecutor.execute(
+                                    () -> job.mListener.onTranscodingFinished(job));
+                        }
+                    } else if (job.getStatus() == TranscodingJob.STATUS_PENDING
+                            || job.getStatus() == TranscodingJob.STATUS_PAUSED) {
+                        // Add the job to retryJobs to handle them later.
+                        retryJobs.add(job);
+                    }
+                }
+            }
+
+            // Try to register with the service once it boots up.
+            IMediaTranscodingService service = getService(true /*retry*/);
+            boolean haveTranscodingClient = false;
+            if (service != null) {
+                synchronized (mLock) {
+                    mTranscodingClient = registerClient(service);
+                    if (mTranscodingClient != null) {
+                        haveTranscodingClient = true;
+                    }
+                }
+            }
+
+            for (TranscodingJob job : retryJobs) {
+                // Notify the job failure if we fails to connect to the service or fail
+                // to retry the job.
+                if (!haveTranscodingClient || !job.retry()) {
+                    // TODO(hkuang): Return correct error code to the client.
+                    handleTranscodingFailed(job.getJobId(), 0 /*unused */);
+                }
+            }
+        });
+    }
+
     private void updateStatus(int jobId, int status) {
         synchronized (mPendingTranscodingJobs) {
             final TranscodingJob job = mPendingTranscodingJobs.get(jobId);
@@ -336,6 +458,30 @@
                 }
             };
 
+    private ITranscodingClient registerClient(IMediaTranscodingService service)
+            throws UnsupportedOperationException {
+        synchronized (mLock) {
+            try {
+                // Registers the client with MediaTranscoding service.
+                mTranscodingClient = service.registerClient(
+                        mTranscodingClientCallback,
+                        mPackageName,
+                        mPackageName,
+                        IMediaTranscodingService.USE_CALLING_UID,
+                        IMediaTranscodingService.USE_CALLING_PID);
+
+                if (mTranscodingClient != null) {
+                    mTranscodingClient.asBinder().linkToDeath(() -> onClientDied(), /* flags */ 0);
+                }
+                return mTranscodingClient;
+            } catch (RemoteException re) {
+                Log.e(TAG, "Failed to register new client due to exception " + re);
+                mTranscodingClient = null;
+            }
+        }
+        throw new UnsupportedOperationException("Failed to register new client");
+    }
+
     /* Private constructor. */
     private MediaTranscodeManager(@NonNull Context context,
             IMediaTranscodingService transcodingService) {
@@ -344,21 +490,17 @@
         mPackageName = mContext.getPackageName();
         mPid = Os.getuid();
         mUid = Os.getpid();
-
-        try {
-            // Registers the client with MediaTranscoding service.
-            mTranscodingClient = transcodingService.registerClient(
-                    mTranscodingClientCallback,
-                    mPackageName,
-                    mPackageName,
-                    IMediaTranscodingService.USE_CALLING_UID,
-                    IMediaTranscodingService.USE_CALLING_PID);
-        } catch (RemoteException re) {
-            Log.e(TAG, "Failed to register new client due to exception " + re);
-            throw new UnsupportedOperationException("Failed to register new client");
-        }
+        mTranscodingClient = registerClient(transcodingService);
     }
 
+    @Override
+    protected void finalize() {
+        try {
+            release();
+        } catch (Exception ex) {
+            Log.e(TAG, "Failed to release");
+        }
+    }
 
     public static final class TranscodingRequest {
         /** Uri of the source media file. */
@@ -807,6 +949,16 @@
         }
 
         /**
+         * Resubmit the transcoding job to the service.
+         *
+         * @return true if successfully resubmit the job to the service. False otherwise.
+         */
+        public synchronized boolean retry() {
+            // TODO(hkuang): Implement this.
+            return true;
+        }
+
+        /**
          * Cancels the transcoding job and notify the listener.
          * If the job happened to finish before being canceled this call is effectively a no-op and
          * will not update the result in that case.
@@ -879,9 +1031,15 @@
      */
     public static MediaTranscodeManager getInstance(@NonNull Context context) {
         // Acquires the MediaTranscoding service.
-        IMediaTranscodingService service = IMediaTranscodingService.Stub.asInterface(
-                ServiceManager.getService(MEDIA_TRANSCODING_SERVICE));
+        IMediaTranscodingService service = getService(false /*retry*/);
+        return getInstance(context, service);
+    }
 
+    /** Similar as above, but wait till the service is ready. */
+    @VisibleForTesting
+    public static MediaTranscodeManager getInstance(@NonNull Context context, boolean retry) {
+        // Acquires the MediaTranscoding service.
+        IMediaTranscodingService service = getService(retry);
         return getInstance(context, service);
     }
 
@@ -896,6 +1054,7 @@
                 sMediaTranscodeManager = new MediaTranscodeManager(context.getApplicationContext(),
                         transcodingService);
             }
+
             return sMediaTranscodeManager;
         }
     }
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 6c41f7b..549e793 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -103,6 +103,8 @@
      * System only flag for a session that needs to have priority over all other
      * sessions. This flag ensures this session will receive media button events
      * regardless of the current ordering in the system.
+     * If there are two or more sessions with this flag, the last session that sets this flag
+     * will be the global priority session.
      *
      * @hide
      */
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 5770c67..5db6729 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -2060,7 +2060,7 @@
                     env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I")));
     FrontendInnerFec innerFec =
             static_cast<FrontendInnerFec>(
-                    env->GetLongField(settings, env->GetFieldID(clazz, "mFec", "J")));
+                    env->GetLongField(settings, env->GetFieldID(clazz, "mInnerFec", "J")));
     uint32_t symbolRate =
             static_cast<uint32_t>(
                     env->GetIntField(settings, env->GetFieldID(clazz, "mSymbolRate", "I")));
@@ -2069,7 +2069,7 @@
                     env->GetIntField(settings, env->GetFieldID(clazz, "mOuterFec", "I")));
     FrontendDvbcAnnex annex =
             static_cast<FrontendDvbcAnnex>(
-                    env->GetByteField(settings, env->GetFieldID(clazz, "mAnnex", "B")));
+                    env->GetIntField(settings, env->GetFieldID(clazz, "mAnnex", "I")));
     FrontendDvbcSpectralInversion spectralInversion =
             static_cast<FrontendDvbcSpectralInversion>(
                     env->GetIntField(
diff --git a/media/tests/MediaTranscodingTest/Android.bp b/media/tests/MediaTranscodingTest/Android.bp
index fcf8f11..907c3c8 100644
--- a/media/tests/MediaTranscodingTest/Android.bp
+++ b/media/tests/MediaTranscodingTest/Android.bp
@@ -8,6 +8,7 @@
     static_libs: [
         "androidx.test.ext.junit",
         "androidx.test.rules",
+        "androidx.test.uiautomator_uiautomator",
         "android-support-test",
         "testng"
     ],
diff --git a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
index 8fe1088..009a41e 100644
--- a/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
+++ b/media/tests/MediaTranscodingTest/src/com/android/mediatranscodingtest/MediaTranscodeManagerTest.java
@@ -23,15 +23,23 @@
 import android.media.MediaTranscodeManager.TranscodingJob;
 import android.media.MediaTranscodeManager.TranscodingRequest;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
 
+import androidx.test.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
+
 import org.junit.Test;
 
+import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -127,8 +135,9 @@
         super.setUp();
 
         mContext = getInstrumentation().getContext();
-        mMediaTranscodeManager = MediaTranscodeManager.getInstance(mContext);
+        mMediaTranscodeManager = MediaTranscodeManager.getInstance(mContext, true /*retry*/);
         assertNotNull(mMediaTranscodeManager);
+        androidx.test.InstrumentationRegistry.registerInstance(getInstrumentation(), new Bundle());
 
         // Setup source HEVC file uri.
         mSourceHEVCVideoUri = resourceToUri(mContext, R.raw.VideoOnlyHEVC, "VideoOnlyHEVC.mp4");
@@ -148,8 +157,6 @@
 
     @Test
     public void testTranscodingFromHevcToAvc() throws Exception {
-        Log.d(TAG, "Starting: testMediaTranscodeManager");
-
         Semaphore transcodeCompleteSemaphore = new Semaphore(0);
 
         // Create a file Uri: file:///data/user/0/com.android.mediatranscodingtest/cache/temp.mp4
@@ -194,6 +201,7 @@
                 stats.mAveragePSNR >= PSNR_THRESHOLD);
     }
 
+
     @Test
     public void testCancelTranscoding() throws Exception {
         Log.d(TAG, "Starting: testMediaTranscodeManager");
@@ -300,5 +308,94 @@
         assertTrue("Failed to receive at least 10 progress updates",
                 progressUpdateCount.get() > 10);
     }
+
+    // [[ $(adb shell whoami) == "root" ]]
+    private boolean checkIfRoot() throws IOException {
+        try (ParcelFileDescriptor result = getInstrumentation().getUiAutomation()
+                .executeShellCommand("whoami");
+             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
+                     new FileInputStream(result.getFileDescriptor())))) {
+            String line;
+            while ((line = bufferedReader.readLine()) != null) {
+                if (line.contains("root")) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private String executeShellCommand(String cmd) throws Exception {
+        return UiDevice.getInstance(
+                InstrumentationRegistry.getInstrumentation()).executeShellCommand(cmd);
+    }
+
+    @Test
+    public void testHandleTranscoderServiceDied() throws Exception {
+        try {
+            if (!checkIfRoot()) {
+                throw new AssertionError("must be root to run this test; try adb root?");
+            } else {
+                Log.i(TAG, "Device is root");
+            }
+        } catch (IOException e) {
+            throw new AssertionError(e);
+        }
+
+        Semaphore transcodeCompleteSemaphore = new Semaphore(0);
+        Semaphore jobStartedSemaphore = new Semaphore(0);
+
+        // Transcode a 15 seconds video, so that the transcoding is not finished when we kill the
+        // service.
+        Uri srcUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+                + mContext.getCacheDir().getAbsolutePath() + "/longtest_15s.mp4");
+        Uri destinationUri = Uri.parse(ContentResolver.SCHEME_FILE + "://"
+                + mContext.getCacheDir().getAbsolutePath() + "/HevcTranscode.mp4");
+
+        TranscodingRequest request =
+                new TranscodingRequest.Builder()
+                        .setSourceUri(mSourceHEVCVideoUri)
+                        .setDestinationUri(destinationUri)
+                        .setType(MediaTranscodeManager.TRANSCODING_TYPE_VIDEO)
+                        .setPriority(MediaTranscodeManager.PRIORITY_REALTIME)
+                        .setVideoTrackFormat(createMediaFormat())
+                        .build();
+        Executor listenerExecutor = Executors.newSingleThreadExecutor();
+
+        Log.i(TAG, "transcoding to " + createMediaFormat());
+
+        TranscodingJob job = mMediaTranscodeManager.enqueueRequest(request, listenerExecutor,
+                transcodingJob -> {
+                    Log.d(TAG, "Transcoding completed with result: " + transcodingJob.getResult());
+                    assertEquals(transcodingJob.getResult(), TranscodingJob.RESULT_ERROR);
+                    transcodeCompleteSemaphore.release();
+                });
+        assertNotNull(job);
+
+        AtomicInteger progressUpdateCount = new AtomicInteger(0);
+
+        // Set progress update executor and use the same executor as result listener.
+        job.setOnProgressUpdateListener(listenerExecutor,
+                new TranscodingJob.OnProgressUpdateListener() {
+                    @Override
+                    public void onProgressUpdate(int newProgress) {
+                        if (newProgress > 0) {
+                            jobStartedSemaphore.release();
+                        }
+                    }
+                });
+
+        // Wait for progress update so the job is in running state.
+        jobStartedSemaphore.tryAcquire(TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue("Job is not running", job.getStatus() == TranscodingJob.STATUS_RUNNING);
+
+        // Kills the service and expects receiving failure of the job.
+        executeShellCommand("pkill -f media.transcoding");
+
+        Log.d(TAG, "testMediaTranscodeManager - Waiting for transcode result.");
+        boolean finishedOnTime = transcodeCompleteSemaphore.tryAcquire(
+                TRANSCODE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        assertTrue("Invalid job status", job.getStatus() == TranscodingJob.STATUS_FINISHED);
+    }
 }
 
diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt
index e90fbd4..e1b3151 100644
--- a/non-updatable-api/current.txt
+++ b/non-updatable-api/current.txt
@@ -11617,6 +11617,16 @@
     field public int version;
   }
 
+  public final class FileChecksum implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getKind();
+    method @Nullable public java.security.cert.Certificate getSourceCertificate() throws java.security.cert.CertificateException;
+    method @Nullable public String getSplitName();
+    method @NonNull public byte[] getValue();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.FileChecksum> CREATOR;
+  }
+
   public final class InstallSourceInfo implements android.os.Parcelable {
     method public int describeContents();
     method @Nullable public String getInitiatingPackageName();
@@ -11901,8 +11911,8 @@
     field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR;
     field public static final int INVALID_ID = -1; // 0xffffffff
     field public static final int STAGED_SESSION_ACTIVATION_FAILED = 2; // 0x2
+    field public static final int STAGED_SESSION_CONFLICT = 4; // 0x4
     field public static final int STAGED_SESSION_NO_ERROR = 0; // 0x0
-    field public static final int STAGED_SESSION_OTHER_ERROR = 4; // 0x4
     field public static final int STAGED_SESSION_UNKNOWN = 3; // 0x3
     field public static final int STAGED_SESSION_VERIFICATION_FAILED = 1; // 0x1
   }
@@ -11992,6 +12002,7 @@
     method @Nullable public abstract android.graphics.drawable.Drawable getApplicationLogo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public CharSequence getBackgroundPermissionOptionLabel();
     method @Nullable public abstract android.content.pm.ChangedPackages getChangedPackages(@IntRange(from=0) int);
+    method public void getChecksums(@NonNull String, boolean, int, @Nullable java.util.List<java.security.cert.Certificate>, @NonNull android.content.IntentSender) throws java.security.cert.CertificateEncodingException, java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
     method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
@@ -12082,6 +12093,7 @@
     field public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3; // 0x3
     field public static final int COMPONENT_ENABLED_STATE_ENABLED = 1; // 0x1
     field public static final int DONT_KILL_APP = 1; // 0x1
+    field public static final String EXTRA_CHECKSUMS = "android.content.pm.extra.CHECKSUMS";
     field public static final String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
     field public static final String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
     field public static final String FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS = "android.software.activities_on_secondary_displays";
@@ -12236,6 +12248,8 @@
     field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
     field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
+    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA256 = 32; // 0x20
+    field public static final int PARTIAL_MERKLE_ROOT_1M_SHA512 = 64; // 0x40
     field public static final int PERMISSION_DENIED = -1; // 0xffffffff
     field public static final int PERMISSION_GRANTED = 0; // 0x0
     field public static final int SIGNATURE_FIRST_NOT_SIGNED = -1; // 0xffffffff
@@ -12245,9 +12259,16 @@
     field public static final int SIGNATURE_SECOND_NOT_SIGNED = -2; // 0xfffffffe
     field public static final int SIGNATURE_UNKNOWN_PACKAGE = -4; // 0xfffffffc
     field public static final int SYNCHRONOUS = 2; // 0x2
+    field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL;
+    field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE;
     field public static final int VERIFICATION_ALLOW = 1; // 0x1
     field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
     field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff
+    field public static final int WHOLE_MD5 = 2; // 0x2
+    field public static final int WHOLE_MERKLE_ROOT_4K_SHA256 = 1; // 0x1
+    field public static final int WHOLE_SHA1 = 4; // 0x4
+    field public static final int WHOLE_SHA256 = 8; // 0x8
+    field public static final int WHOLE_SHA512 = 16; // 0x10
   }
 
   public static class PackageManager.NameNotFoundException extends android.util.AndroidException {
@@ -14250,6 +14271,10 @@
     enum_constant public static final android.graphics.BlurMaskFilter.Blur SOLID;
   }
 
+  public final class BlurShader extends android.graphics.Shader {
+    ctor public BlurShader(float, float, @Nullable android.graphics.Shader);
+  }
+
   public class Camera {
     ctor public Camera();
     method public void applyToCanvas(android.graphics.Canvas);
@@ -35282,9 +35307,11 @@
   public final class PowerManager {
     method public void addThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener);
     method public void addThermalStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.PowerManager.OnThermalStatusChangedListener);
+    method @Nullable public java.time.Duration getBatteryDischargePrediction();
     method public int getCurrentThermalStatus();
     method public int getLocationPowerSaveMode();
     method public float getThermalHeadroom(@IntRange(from=0, to=60) int);
+    method public boolean isBatteryDischargePredictionPersonalized();
     method public boolean isDeviceIdleMode();
     method public boolean isIgnoringBatteryOptimizations(String);
     method public boolean isInteractive();
@@ -45986,6 +46013,7 @@
     method public void onDataConnectionStateChanged(int);
     method public void onDataConnectionStateChanged(int, int);
     method @RequiresPermission("android.permission.READ_PHONE_STATE") public void onDisplayInfoChanged(@NonNull android.telephony.TelephonyDisplayInfo);
+    method public void onEmergencyNumberListChanged(@NonNull java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>>);
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo);
     method public void onMessageWaitingIndicatorChanged(boolean);
     method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public void onPreciseDataConnectionStateChanged(@NonNull android.telephony.PreciseDataConnectionState);
@@ -46502,7 +46530,9 @@
     method @Deprecated public String iccTransmitApduBasicChannel(int, int, int, int, int, String);
     method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
     method public boolean isConcurrentVoiceAndDataSupported();
+    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isDataConnectionAllowed();
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled();
+    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledForReason(int);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
     method public boolean isEmergencyNumber(@NonNull String);
     method public boolean isHearingAidCompatibilitySupported();
@@ -46524,6 +46554,7 @@
     method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
     method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledForReason(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setForbiddenPlmns(@NonNull java.util.List<java.lang.String>);
     method public boolean setLine1NumberForDisplay(String, String);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setNetworkSelectionModeAutomatic();
@@ -46571,6 +46602,10 @@
     field public static final int DATA_CONNECTING = 1; // 0x1
     field public static final int DATA_DISCONNECTED = 0; // 0x0
     field public static final int DATA_DISCONNECTING = 4; // 0x4
+    field public static final int DATA_ENABLED_REASON_CARRIER = 2; // 0x2
+    field public static final int DATA_ENABLED_REASON_POLICY = 1; // 0x1
+    field public static final int DATA_ENABLED_REASON_THERMAL = 3; // 0x3
+    field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
     field public static final int DATA_SUSPENDED = 3; // 0x3
     field public static final int DATA_UNKNOWN = -1; // 0xffffffff
     field public static final String EXTRA_ACTIVE_SIM_SUPPORTED_COUNT = "android.telephony.extra.ACTIVE_SIM_SUPPORTED_COUNT";
@@ -53328,7 +53363,7 @@
   }
 
   public class ViewPropertyAnimator {
-    method public android.view.ViewPropertyAnimator alpha(float);
+    method public android.view.ViewPropertyAnimator alpha(@FloatRange(from=0.0f, to=1.0f) float);
     method public android.view.ViewPropertyAnimator alphaBy(float);
     method public void cancel();
     method public long getDuration();
diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt
index 3fd0ee1..7cce0f2 100644
--- a/non-updatable-api/system-current.txt
+++ b/non-updatable-api/system-current.txt
@@ -219,6 +219,7 @@
     field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
     field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
     field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
+    field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE";
     field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES";
     field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME";
     field public static final String SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON = "android.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON";
@@ -7298,6 +7299,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressedForToken(@NonNull String);
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSaveEnabled(boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setAdaptivePowerSavePolicy(@NonNull android.os.BatterySaverPolicyConfig);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean);
     method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setDynamicPowerSaveHint(boolean, int);
     method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveModeEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void suppressAmbientDisplay(@NonNull String, boolean);
@@ -9682,7 +9684,8 @@
 
   public class PhoneStateListener {
     method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes);
-    method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
+    method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber);
+    method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int);
     method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber);
     method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState);
     method public void onRadioPowerStateChanged(int);
@@ -10099,10 +10102,8 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionAllowed();
     method public boolean isDataConnectivityPossible();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
-    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledWithReason(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
@@ -10134,7 +10135,6 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledWithReason(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
@@ -10172,10 +10172,6 @@
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
     field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
-    field public static final int DATA_ENABLED_REASON_CARRIER = 2; // 0x2
-    field public static final int DATA_ENABLED_REASON_POLICY = 1; // 0x1
-    field public static final int DATA_ENABLED_REASON_THERMAL = 3; // 0x3
-    field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
     field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
     field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
     field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
@@ -11585,8 +11581,8 @@
   public interface PacProcessor {
     method @Nullable public String findProxyForUrl(@NonNull String);
     method @NonNull public static android.webkit.PacProcessor getInstance();
-    method @NonNull public static android.webkit.PacProcessor getInstanceForNetwork(long);
-    method public default long getNetworkHandle();
+    method @NonNull public static android.webkit.PacProcessor getInstanceForNetwork(@Nullable android.net.Network);
+    method @Nullable public default android.net.Network getNetwork();
     method public default void releasePacProcessor();
     method public boolean setProxyScript(@NonNull String);
   }
@@ -11727,7 +11723,7 @@
     method public android.webkit.CookieManager getCookieManager();
     method public android.webkit.GeolocationPermissions getGeolocationPermissions();
     method @NonNull public default android.webkit.PacProcessor getPacProcessor();
-    method @NonNull public default android.webkit.PacProcessor getPacProcessorForNetwork(long);
+    method @NonNull public default android.webkit.PacProcessor getPacProcessorForNetwork(@Nullable android.net.Network);
     method public android.webkit.ServiceWorkerController getServiceWorkerController();
     method public android.webkit.WebViewFactoryProvider.Statics getStatics();
     method @Deprecated public android.webkit.TokenBindingService getTokenBindingService();
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 039f2c0..d3277de 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -121,7 +121,6 @@
     <string-array name="config_systemUIServiceComponentsExclude" translatable="false">
         <item>com.android.systemui.recents.Recents</item>
         <item>com.android.systemui.volume.VolumeUI</item>
-        <item>com.android.systemui.stackdivider.Divider</item>
         <item>com.android.systemui.statusbar.phone.StatusBar</item>
         <item>com.android.systemui.keyboard.KeyboardUI</item>
         <item>com.android.systemui.pip.PipUI</item>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java
index 552cadf..b17ad0f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java
@@ -16,7 +16,9 @@
 
 package com.android.systemui;
 
+import com.android.systemui.dagger.GlobalModule;
 import com.android.systemui.dagger.GlobalRootComponent;
+import com.android.systemui.dagger.WMModule;
 
 import javax.inject.Singleton;
 
@@ -26,7 +28,9 @@
 @Singleton
 @Component(
         modules = {
-                CarSysUIComponentModule.class
+                GlobalModule.class,
+                CarSysUIComponentModule.class,
+                WMModule.class
         })
 public interface CarGlobalRootComponent extends GlobalRootComponent {
     /**
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java
index 24d9d09..1a6fdfa 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSysUIComponent.java
@@ -16,11 +16,9 @@
 
 package com.android.systemui;
 
-import com.android.systemui.dagger.DependencyBinder;
 import com.android.systemui.dagger.DependencyProvider;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.SystemServicesModule;
 import com.android.systemui.dagger.SystemUIModule;
 import com.android.systemui.pip.phone.dagger.PipModule;
 
@@ -33,9 +31,7 @@
 @Subcomponent(modules = {
         CarComponentBinder.class,
         DependencyProvider.class,
-        DependencyBinder.class,
         PipModule.class,
-        SystemServicesModule.class,
         SystemUIModule.class,
         CarSystemUIModule.class,
         CarSystemUIBinder.class})
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
index 797a178..3971e18 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
@@ -34,7 +34,6 @@
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsModule;
 import com.android.systemui.shortcut.ShortcutKeyDispatcher;
-import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.dagger.StatusBarModule;
 import com.android.systemui.statusbar.notification.InstantAppNotifier;
 import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
@@ -42,6 +41,7 @@
 import com.android.systemui.theme.ThemeOverlayController;
 import com.android.systemui.toast.ToastUI;
 import com.android.systemui.util.leak.GarbageMonitor;
+import com.android.systemui.wmshell.WMShell;
 
 import dagger.Binds;
 import dagger.Module;
@@ -59,12 +59,6 @@
     @ClassKey(AuthController.class)
     public abstract SystemUI bindAuthController(AuthController sysui);
 
-    /** Inject into Divider. */
-    @Binds
-    @IntoMap
-    @ClassKey(Divider.class)
-    public abstract SystemUI bindDivider(Divider sysui);
-
     /** Inject Car Navigation Bar. */
     @Binds
     @IntoMap
@@ -192,4 +186,10 @@
     @IntoMap
     @ClassKey(SideLoadedAppController.class)
     public abstract SystemUI bindSideLoadedAppController(SideLoadedAppController sysui);
+
+    /** Inject into WMShell. */
+    @Binds
+    @IntoMap
+    @ClassKey(WMShell.class)
+    public abstract SystemUI bindWMShell(WMShell sysui);
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
index 290700f..3eea513 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java
@@ -47,7 +47,6 @@
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImplementation;
-import com.android.systemui.stackdivider.DividerModule;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
@@ -75,7 +74,6 @@
 
 @Module(
         includes = {
-                DividerModule.class,
                 QSModule.class,
                 CarWMShellModule.class
         })
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
index 143c444f..b8bf825 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/navigationbar/SystemBarConfigs.java
@@ -29,6 +29,8 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
+import com.android.systemui.car.notification.BottomNotificationPanelViewMediator;
+import com.android.systemui.car.notification.TopNotificationPanelViewMediator;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 
@@ -95,6 +97,7 @@
         populateMaps();
         readConfigs();
         checkEnabledBarsHaveUniqueBarTypes();
+        checkSystemBarEnabledForNotificationPanel();
         setInsetPaddingsForOverlappingCorners();
         sortSystemBarSidesByZOrder();
     }
@@ -221,6 +224,34 @@
         }
     }
 
+    private void checkSystemBarEnabledForNotificationPanel() throws RuntimeException {
+
+        String notificationPanelMediatorName =
+                mResources.getString(R.string.config_notificationPanelViewMediator);
+        if (notificationPanelMediatorName == null) {
+            return;
+        }
+
+        Class<?> notificationPanelMediatorUsed = null;
+        try {
+            notificationPanelMediatorUsed = Class.forName(notificationPanelMediatorName);
+        } catch (ClassNotFoundException e) {
+            e.printStackTrace();
+        }
+
+        if (!mTopNavBarEnabled && notificationPanelMediatorUsed.isAssignableFrom(
+                TopNotificationPanelViewMediator.class)) {
+            throw new RuntimeException(
+                    "Top System Bar must be enabled to use " + notificationPanelMediatorName);
+        }
+
+        if (!mBottomNavBarEnabled && notificationPanelMediatorUsed.isAssignableFrom(
+                BottomNotificationPanelViewMediator.class)) {
+            throw new RuntimeException("Bottom System Bar must be enabled to use "
+                    + notificationPanelMediatorName);
+        }
+    }
+
     private void setInsetPaddingsForOverlappingCorners() {
         setInsetPaddingForOverlappingCorner(TOP, LEFT);
         setInsetPaddingForOverlappingCorner(TOP, RIGHT);
@@ -277,7 +308,7 @@
     }
 
     private static boolean isHorizontalBar(@SystemBarSide int side) {
-        return  side == TOP || side == BOTTOM;
+        return side == TOP || side == BOTTOM;
     }
 
     private static boolean isVerticalBar(@SystemBarSide int side) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
index 3b22fdb..38e1a48 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -299,10 +299,10 @@
         // The glass pane is used to view touch events before passed to the notification list.
         // This allows us to initialize gesture listeners and detect when to close the notifications
         glassPane.setOnTouchListener((v, event) -> {
-            if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+            if (isClosingAction(event)) {
                 mNotificationListAtEndAtTimeOfTouch = false;
             }
-            if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            if (isOpeningAction(event)) {
                 mFirstTouchDownOnGlassPane = event.getRawX();
                 mNotificationListAtEndAtTimeOfTouch = mNotificationListAtEnd;
                 // Reset the tracker when there is a touch down on the glass pane.
@@ -355,8 +355,7 @@
             if (rect != null) {
                 clippedHeight = rect.bottom;
             }
-            if (!handled && event.getActionMasked() == MotionEvent.ACTION_UP
-                    && mIsSwipingVerticallyToClose) {
+            if (!handled && isClosingAction(event) && mIsSwipingVerticallyToClose) {
                 if (getSettleClosePercentage() < getPercentageFromEndingEdge() && isTracking) {
                     animatePanel(DEFAULT_FLING_VELOCITY, false);
                 } else if (clippedHeight != getLayout().getHeight() && isTracking) {
@@ -369,7 +368,7 @@
             // Updating the mNotificationListAtEndAtTimeOfTouch state has to be done after
             // the event has been passed to the closeGestureDetector above, such that the
             // closeGestureDetector sees the up event before the state has changed.
-            if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+            if (isClosingAction(event)) {
                 mNotificationListAtEndAtTimeOfTouch = false;
             }
             return handled || isTracking;
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
index 45808a8..bde31f1 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayPanelViewController.java
@@ -191,6 +191,38 @@
         }
     }
 
+    /** Checks if a {@link MotionEvent} is an action to open the panel.
+     * @param e {@link MotionEvent} to check.
+     * @return true only if opening action.
+     */
+    protected boolean isOpeningAction(MotionEvent e) {
+        if (mAnimateDirection == POSITIVE_DIRECTION) {
+            return e.getActionMasked() == MotionEvent.ACTION_DOWN;
+        }
+
+        if (mAnimateDirection == NEGATIVE_DIRECTION) {
+            return e.getActionMasked() == MotionEvent.ACTION_UP;
+        }
+
+        return false;
+    }
+
+    /** Checks if a {@link MotionEvent} is an action to close the panel.
+     * @param e {@link MotionEvent} to check.
+     * @return true only if closing action.
+     */
+    protected boolean isClosingAction(MotionEvent e) {
+        if (mAnimateDirection == POSITIVE_DIRECTION) {
+            return e.getActionMasked() == MotionEvent.ACTION_UP;
+        }
+
+        if (mAnimateDirection == NEGATIVE_DIRECTION) {
+            return e.getActionMasked() == MotionEvent.ACTION_DOWN;
+        }
+
+        return false;
+    }
+
     /* ***************************************************************************************** *
      * Panel Animation
      * ***************************************************************************************** */
@@ -243,8 +275,7 @@
      * Depending on certain conditions, determines whether to fully expand or collapse the panel.
      */
     protected void maybeCompleteAnimation(MotionEvent event) {
-        if (event.getActionMasked() == MotionEvent.ACTION_UP
-                && isPanelVisible()) {
+        if (isClosingAction(event) && isPanelVisible()) {
             if (mSettleClosePercentage < mPercentageFromEndingEdge) {
                 animatePanel(DEFAULT_FLING_VELOCITY, false);
             } else {
diff --git a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
index e493c97..c7354ed 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wm/DisplaySystemBarsController.java
@@ -49,7 +49,7 @@
     private final Context mContext;
     private SparseArray<PerDisplay> mPerDisplaySparseArray;
 
-    private DisplaySystemBarsController(
+    public DisplaySystemBarsController(
             Context context,
             IWindowManager wmService,
             DisplayController displayController,
@@ -167,33 +167,4 @@
             }
         }
     }
-
-    /** Builds {@link DisplaySystemBarsController} instance. */
-    public static class Builder {
-        private Context mContext;
-        private IWindowManager mWmService;
-        private DisplayController mDisplayController;
-        private Handler mHandler;
-        private TransactionPool mTransactionPool;
-
-        public Builder(Context context, IWindowManager wmService,
-                DisplayController displayController, Handler handler,
-                TransactionPool transactionPool) {
-            mContext = context;
-            mWmService = wmService;
-            mDisplayController = displayController;
-            mHandler = handler;
-            mTransactionPool = transactionPool;
-        }
-
-        /** Builds and initializes {@link DisplaySystemBarsController} instance. */
-        public DisplaySystemBarsController build() {
-            DisplaySystemBarsController displaySystemBarsController =
-                    new DisplaySystemBarsController(
-                            mContext, mWmService, mDisplayController, mHandler, mTransactionPool);
-            // Separates startMonitorDisplays from constructor to prevent circular init issue.
-            displaySystemBarsController.startMonitorDisplays();
-            return displaySystemBarsController;
-        }
-    }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
index 2324c3d..6d31a8d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java
@@ -22,8 +22,6 @@
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.pip.phone.PipMenuActivity;
-import com.android.systemui.pip.phone.dagger.PipMenuActivityClass;
 import com.android.systemui.wm.DisplaySystemBarsController;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
@@ -40,15 +38,7 @@
     DisplayImeController provideDisplayImeController(Context context,
             IWindowManager wmService, DisplayController displayController,
             @Main Handler mainHandler, TransactionPool transactionPool) {
-        return new DisplaySystemBarsController.Builder(context, wmService, displayController,
-                mainHandler, transactionPool).build();
-    }
-
-    /** TODO(b/150319024): PipMenuActivity will move to a Window */
-    @SysUISingleton
-    @PipMenuActivityClass
-    @Provides
-    Class<?> providePipMenuActivityClass() {
-        return PipMenuActivity.class;
+        return new DisplaySystemBarsController(context, wmService, displayController,
+                mainHandler, transactionPool);
     }
 }
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
index b65578d..391f75e 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/wm/DisplaySystemBarsControllerTest.java
@@ -64,13 +64,13 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
-        mController = new DisplaySystemBarsController.Builder(
+        mController = new DisplaySystemBarsController(
                 mContext,
                 mIWindowManager,
                 mDisplayController,
                 mHandler,
                 mTransactionPool
-        ).build();
+        );
     }
 
     @Test
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index f80e934..9ff8684 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -352,16 +352,18 @@
             if (powerManager != null) {
                 powerManager.reboot("dynsystem");
             }
-        } else {
-            Log.e(TAG, "Failed to enable DynamicSystem because of native runtime error.");
-            mNM.cancel(NOTIFICATION_ID);
-
-            Toast.makeText(this,
-                    getString(R.string.toast_failed_to_reboot_to_dynsystem),
-                    Toast.LENGTH_LONG).show();
-
-            mDynSystem.remove();
+            return;
         }
+
+        Log.e(TAG, "Failed to enable DynamicSystem because of native runtime error.");
+
+        Toast.makeText(this,
+                getString(R.string.toast_failed_to_reboot_to_dynsystem),
+                Toast.LENGTH_LONG).show();
+
+        postStatus(STATUS_NOT_STARTED, CAUSE_ERROR_EXCEPTION, null);
+        resetTaskAndStop();
+        mDynSystem.remove();
     }
 
     private void executeRebootToNormalCommand() {
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index f42bf19..11d1b0a 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -483,6 +483,13 @@
     }
 
     @Override
+    protected void onDocIdDeleted(String docId) {
+        Uri uri = DocumentsContract.buildDocumentUri(AUTHORITY, docId);
+        getContext().revokeUriPermission(uri, ~0);
+    }
+
+
+    @Override
     public Cursor queryRoots(String[] projection) throws FileNotFoundException {
         final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection));
         synchronized (mRootsLock) {
diff --git a/packages/PrintSpooler/res/values-fa/strings.xml b/packages/PrintSpooler/res/values-fa/strings.xml
index 596947d..719fc92 100644
--- a/packages/PrintSpooler/res/values-fa/strings.xml
+++ b/packages/PrintSpooler/res/values-fa/strings.xml
@@ -31,7 +31,7 @@
     <string name="template_all_pages" msgid="3322235982020148762">"همه <xliff:g id="PAGE_COUNT">%1$s</xliff:g> صفحه"</string>
     <string name="template_page_range" msgid="428638530038286328">"محدوده <xliff:g id="PAGE_COUNT">%1$s</xliff:g> صفحه"</string>
     <string name="pages_range_example" msgid="8558694453556945172">"‏‏‎مثلاً ۱—۵،‏۹،۷—۱۰"</string>
-    <string name="print_preview" msgid="8010217796057763343">"پیش‌نمایش چاپ"</string>
+    <string name="print_preview" msgid="8010217796057763343">"پیش‌نمای چاپ"</string>
     <string name="install_for_print_preview" msgid="6366303997385509332">"‏نصب نمایشگر PDF برای پیش‌نمایش"</string>
     <string name="printing_app_crashed" msgid="854477616686566398">"برنامه چاپ خراب شد"</string>
     <string name="generating_print_job" msgid="3119608742651698916">"در حال ایجاد کار چاپ"</string>
diff --git a/packages/PrintSpooler/res/values-uz/strings.xml b/packages/PrintSpooler/res/values-uz/strings.xml
index d17bce7..ea0a6ea 100644
--- a/packages/PrintSpooler/res/values-uz/strings.xml
+++ b/packages/PrintSpooler/res/values-uz/strings.xml
@@ -100,7 +100,7 @@
   </string-array>
   <string-array name="orientation_labels">
     <item msgid="4061931020926489228">"Tik holat"</item>
-    <item msgid="3199660090246166812">"Eniga"</item>
+    <item msgid="3199660090246166812">"Yotiq"</item>
   </string-array>
     <string name="print_write_error_message" msgid="5787642615179572543">"Faylga yozib bo‘lmadi"</string>
     <string name="print_error_default_message" msgid="8602678405502922346">"Xatolik yuz berdi. Qaytadan urining."</string>
diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_0.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_0.xml
new file mode 100644
index 0000000..16e9190
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_0.xml
@@ -0,0 +1,31 @@
+<!--
+    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="25.50"
+    android:viewportHeight="25.50">
+    <group
+        android:translateX="0.77"
+        android:translateY="0.23" >
+        <path
+            android:pathData="M14,12h6.54l3.12,-3.89c0.39,-0.48 0.29,-1.19 -0.22,-1.54C21.67,5.36 17.55,3 12,3C6.44,3 2.33,5.36 0.56,6.57C0.05,6.92 -0.05,7.63 0.33,8.11L11.16,21.6c0.42,0.53 1.23,0.53 1.66,0L14,20.13V12z"
+            android:fillColor="#FFFFFF"/>
+        <path
+            android:pathData="M22.71,15.67l-1.83,1.83l1.83,1.83c0.38,0.38 0.38,1 0,1.38v0c-0.38,0.38 -1,0.39 -1.38,0l-1.83,-1.83l-1.83,1.83c-0.38,0.38 -1,0.38 -1.38,0l-0.01,-0.01c-0.38,-0.38 -0.38,-1 0,-1.38l1.83,-1.83l-1.82,-1.82c-0.38,-0.38 -0.38,-1 0,-1.38l0.01,-0.01c0.38,-0.38 1,-0.38 1.38,0l1.82,1.82l1.82,-1.82c0.38,-0.38 1,-0.38 1.38,0l0,0C23.09,14.67 23.09,15.29 22.71,15.67z"
+            android:fillColor="#FFFFFF"/>
+    </group>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_1.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_1.xml
new file mode 100644
index 0000000..4c338c9
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_1.xml
@@ -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.
+-->
+<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,16.41L20.59,15l-2.09,2.09L16.41,15L15,16.41l2.09,2.09L15,20.59L16.41,22l2.09,-2.08L20.59,22L22,20.59l-2.08,-2.09L22,16.41z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2.01C7.25,2.01 2.97,4.09 0,7.4L7.582,16.625C7.582,16.627 7.58,16.629 7.58,16.631L11.99,22L12,22L13,20.789L13,17.641L13,13.119C12.68,13.039 12.34,13 12,13C10.601,13 9.351,13.64 8.531,14.639L2.699,7.539C5.269,5.279 8.58,4.01 12,4.01C15.42,4.01 18.731,5.279 21.301,7.539L16.811,13L19.4,13L24,7.4C21.03,4.09 16.75,2.01 12,2.01z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_2.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_2.xml
new file mode 100644
index 0000000..79037db
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_2.xml
@@ -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.
+-->
+<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,16.41L20.59,15l-2.09,2.09L16.41,15L15,16.41l2.09,2.09L15,20.59L16.41,22l2.09,-2.08L20.59,22L22,20.59l-2.08,-2.09L22,16.41z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2C7.25,2 2.97,4.081 0,7.391L12,22L13,20.779L13,17.631L13,13L16.801,13L18,13L19.391,13L24,7.391C21.03,4.081 16.75,2 12,2zM12,4C14.747,4 17.423,4.819 19.701,6.313C20.259,6.678 20.795,7.085 21.301,7.529L17.389,12.287C16.029,10.868 14.119,9.99 12,9.99C9.88,9.99 7.969,10.869 6.609,12.289L2.699,7.529C5.269,5.269 8.58,4 12,4z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_3.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_3.xml
new file mode 100644
index 0000000..21ad128
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_3.xml
@@ -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.
+-->
+<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,16.41L20.59,15l-2.09,2.09L16.41,15L15,16.41l2.09,2.09L15,20.59L16.41,22l2.09,-2.08L20.59,22L22,20.59l-2.08,-2.09L22,16.41z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M12,2C7.25,2 2.97,4.081 0,7.391L3.301,11.41L12,22L13,20.779L13,17.631L13,13L16.801,13L19.391,13L20.699,11.41C20.699,11.409 20.698,11.409 20.697,11.408L24,7.391C21.03,4.081 16.75,2 12,2zM12,4C15.42,4 18.731,5.269 21.301,7.529L19.35,9.9C17.43,8.1 14.86,6.99 12,6.99C9.14,6.99 6.57,8.1 4.65,9.9C4.65,9.901 4.649,9.902 4.648,9.902L2.699,7.529C5.269,5.269 8.58,4 12,4z"/>
+</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_4.xml b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_4.xml
new file mode 100644
index 0000000..2ec5ba3
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_show_x_wifi_signal_4.xml
@@ -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.
+-->
+<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,2C7.25,2 2.97,4.08 0,7.39L12,22l1,-1.22V13h6.39L24,7.39C21.03,4.08 16.75,2 12,2z"/>
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M22,16.41L20.59,15l-2.09,2.09L16.41,15L15,16.41l2.09,2.09L15,20.59L16.41,22l2.09,-2.08L20.59,22L22,20.59l-2.08,-2.09L22,16.41z"/>
+</vector>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index f3b22d3..5fc311a 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -139,7 +139,7 @@
     <string name="accessibility_wifi_signal_full" msgid="7165262794551355617">"‏قدرت سیگنال Wi‑Fi کامل است."</string>
     <string name="accessibility_wifi_security_type_none" msgid="162352241518066966">"شبکه باز"</string>
     <string name="accessibility_wifi_security_type_secured" msgid="2399774097343238942">"شبکه ایمن"</string>
-    <string name="process_kernel_label" msgid="950292573930336765">"‏سیستم عامل Android"</string>
+    <string name="process_kernel_label" msgid="950292573930336765">"‏سیستم‌عامل Android"</string>
     <string name="data_usage_uninstalled_apps" msgid="1933665711856171491">"برنامه‌های حذف شده"</string>
     <string name="data_usage_uninstalled_apps_users" msgid="5533981546921913295">"برنامه‌ها و کاربران حذف شده"</string>
     <string name="data_usage_ota" msgid="7984667793701597001">"به‌روزرسانی‌های سیستم"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index f219d24..8b92640 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -37,7 +37,7 @@
     <string name="wifi_no_internet" msgid="1774198889176926299">"Ինտերնետ կապ չկա"</string>
     <string name="saved_network" msgid="7143698034077223645">"Ով է պահել՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_network_scorer" msgid="7665725527352893558">"Ավտոմատ կերպով կապակցվել է %1$s-ի միջոցով"</string>
-    <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ավտոմատ կերպով միացել է ցանցի վարկանիշի ծառայության մատակարարի միջոցով"</string>
+    <string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ավտոմատ միացել է ցանցերի վարկանիշի մատակարարի միջոցով"</string>
     <string name="connected_via_passpoint" msgid="7735442932429075684">"Միացված է %1$s-ի միջոցով"</string>
     <string name="connected_via_app" msgid="3532267661404276584">"Միացված է <xliff:g id="NAME">%1$s</xliff:g>-ի միջոցով"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Հասանելի է %1$s-ի միջոցով"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 83b72e9..27f5dc9 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -93,8 +93,8 @@
     <string name="bluetooth_profile_sap" msgid="8304170950447934386">"Sim-toegang"</string>
     <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD-audio: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string>
     <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD-audio"</string>
-    <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Gehoorapparaten"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Verbonden met gehoorapparaten"</string>
+    <string name="bluetooth_profile_hearing_aid" msgid="58154575573984914">"Hoortoestellen"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_connected" msgid="8191273236809964030">"Verbonden met hoortoestellen"</string>
     <string name="bluetooth_a2dp_profile_summary_connected" msgid="7422607970115444153">"Verbonden met audio van medium"</string>
     <string name="bluetooth_headset_profile_summary_connected" msgid="2420981566026949688">"Verbonden met audio van telefoon"</string>
     <string name="bluetooth_opp_profile_summary_connected" msgid="2393521801478157362">"Verbonden met server voor bestandsoverdracht"</string>
@@ -111,7 +111,7 @@
     <string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Gebruiken voor audio van telefoon"</string>
     <string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Gebruiken voor bestandsoverdracht"</string>
     <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Gebruiken voor invoer"</string>
-    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Gebruiken voor gehoorapparaten"</string>
+    <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Gebruiken voor hoortoestellen"</string>
     <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Koppelen"</string>
     <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"KOPPELEN"</string>
     <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Annuleren"</string>
@@ -127,8 +127,8 @@
     <string name="bluetooth_talkback_headphone" msgid="8613073829180337091">"Hoofdtelefoon"</string>
     <string name="bluetooth_talkback_input_peripheral" msgid="5133944817800149942">"Randapparaat voor invoer"</string>
     <string name="bluetooth_talkback_bluetooth" msgid="1143241359781999989">"Bluetooth"</string>
-    <string name="bluetooth_hearingaid_left_pairing_message" msgid="8561855779703533591">"Linker gehoorapparaat koppelen…"</string>
-    <string name="bluetooth_hearingaid_right_pairing_message" msgid="2655347721696331048">"Rechter gehoorapparaat koppelen…"</string>
+    <string name="bluetooth_hearingaid_left_pairing_message" msgid="8561855779703533591">"Linker hoortoestel koppelen…"</string>
+    <string name="bluetooth_hearingaid_right_pairing_message" msgid="2655347721696331048">"Rechter hoortoestel koppelen…"</string>
     <string name="bluetooth_hearingaid_left_battery_level" msgid="7375621694748104876">"Links: batterijniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_hearingaid_right_battery_level" msgid="1850094448499089312">"Rechts: batterijniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="accessibility_wifi_off" msgid="1195445715254137155">"Wifi: uitgeschakeld."</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index a43412e..b280806 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -49,11 +49,19 @@
     private static String sSharedSystemSharedLibPackageName;
 
     static final int[] WIFI_PIE = {
-            com.android.internal.R.drawable.ic_wifi_signal_0,
-            com.android.internal.R.drawable.ic_wifi_signal_1,
-            com.android.internal.R.drawable.ic_wifi_signal_2,
-            com.android.internal.R.drawable.ic_wifi_signal_3,
-            com.android.internal.R.drawable.ic_wifi_signal_4
+        com.android.internal.R.drawable.ic_wifi_signal_0,
+        com.android.internal.R.drawable.ic_wifi_signal_1,
+        com.android.internal.R.drawable.ic_wifi_signal_2,
+        com.android.internal.R.drawable.ic_wifi_signal_3,
+        com.android.internal.R.drawable.ic_wifi_signal_4
+    };
+
+    static final int[] SHOW_X_WIFI_PIE = {
+        R.drawable.ic_show_x_wifi_signal_0,
+        R.drawable.ic_show_x_wifi_signal_1,
+        R.drawable.ic_show_x_wifi_signal_2,
+        R.drawable.ic_show_x_wifi_signal_3,
+        R.drawable.ic_show_x_wifi_signal_4
     };
 
     public static void updateLocationEnabled(Context context, boolean enabled, int userId,
@@ -353,10 +361,22 @@
      * @throws IllegalArgumentException if an invalid RSSI level is given.
      */
     public static int getWifiIconResource(int level) {
+        return getWifiIconResource(false /* showX */, level);
+    }
+
+    /**
+     * Returns the Wifi icon resource for a given RSSI level.
+     *
+     * @param showX True if a connected Wi-Fi network has the problem which should show Pie+x
+     *              signal icon to users.
+     * @param level The number of bars to show (0-4)
+     * @throws IllegalArgumentException if an invalid RSSI level is given.
+     */
+    public static int getWifiIconResource(boolean showX, int level) {
         if (level < 0 || level >= WIFI_PIE.length) {
             throw new IllegalArgumentException("No Wifi icon found for level: " + level);
         }
-        return WIFI_PIE[level];
+        return showX ? SHOW_X_WIFI_PIE[level] : WIFI_PIE[level];
     }
 
     public static int getDefaultStorageManagerDaysToRetain(Resources resources) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
index a53bc9f..bba69f2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiEntryPreference.java
@@ -35,6 +35,7 @@
 import com.android.settingslib.R;
 import com.android.settingslib.Utils;
 import com.android.wifitrackerlib.WifiEntry;
+import com.android.wifitrackerlib.WifiEntry.ConnectedInfo;
 
 /**
  * Preference to display a WifiEntry in a wifi picker.
@@ -64,6 +65,7 @@
     private final IconInjector mIconInjector;
     private WifiEntry mWifiEntry;
     private int mLevel = -1;
+    private boolean mShowX; // Shows the Wi-Fi signl icon of Pie+x when it's true.
     private CharSequence mContentDescription;
     private OnButtonClickListener mOnButtonClickListener;
 
@@ -136,9 +138,15 @@
     public void refresh() {
         setTitle(mWifiEntry.getTitle());
         final int level = mWifiEntry.getLevel();
-        if (level != mLevel) {
+        final ConnectedInfo connectedInfo = mWifiEntry.getConnectedInfo();
+        boolean showX = false;
+        if (connectedInfo != null) {
+            showX = !connectedInfo.isDefaultNetwork || !connectedInfo.isValidated;
+        }
+        if (level != mLevel || showX != mShowX) {
             mLevel = level;
-            updateIcon(mLevel);
+            mShowX = showX;
+            updateIcon(mShowX, mLevel);
             notifyChanged();
         }
 
@@ -184,13 +192,13 @@
     }
 
 
-    private void updateIcon(int level) {
+    private void updateIcon(boolean showX, int level) {
         if (level == -1) {
             setIcon(null);
             return;
         }
 
-        final Drawable drawable = mIconInjector.getIcon(level);
+        final Drawable drawable = mIconInjector.getIcon(showX, level);
         if (drawable != null) {
             drawable.setTintList(Utils.getColorAttr(getContext(),
                     android.R.attr.colorControlNormal));
@@ -260,8 +268,8 @@
             mContext = context;
         }
 
-        public Drawable getIcon(int level) {
-            return mContext.getDrawable(Utils.getWifiIconResource(level));
+        public Drawable getIcon(boolean showX, int level) {
+            return mContext.getDrawable(Utils.getWifiIconResource(showX, level));
         }
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
index 46e699d..40af7dc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiEntryPreferenceTest.java
@@ -17,6 +17,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -29,6 +30,7 @@
 
 import com.android.settingslib.R;
 import com.android.wifitrackerlib.WifiEntry;
+import com.android.wifitrackerlib.WifiEntry.ConnectedInfo;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -62,6 +64,17 @@
     @Mock
     private Drawable mMockDrawable4;
 
+    @Mock
+    private Drawable mMockShowXDrawable0;
+    @Mock
+    private Drawable mMockShowXDrawable1;
+    @Mock
+    private Drawable mMockShowXDrawable2;
+    @Mock
+    private Drawable mMockShowXDrawable3;
+    @Mock
+    private Drawable mMockShowXDrawable4;
+
     private static final String MOCK_TITLE = "title";
     private static final String MOCK_SUMMARY = "summary";
     private static final String FAKE_URI_STRING = "fakeuri";
@@ -75,11 +88,22 @@
         when(mMockWifiEntry.getTitle()).thenReturn(MOCK_TITLE);
         when(mMockWifiEntry.getSummary(false /* concise */)).thenReturn(MOCK_SUMMARY);
 
-        when(mMockIconInjector.getIcon(0)).thenReturn(mMockDrawable0);
-        when(mMockIconInjector.getIcon(1)).thenReturn(mMockDrawable1);
-        when(mMockIconInjector.getIcon(2)).thenReturn(mMockDrawable2);
-        when(mMockIconInjector.getIcon(3)).thenReturn(mMockDrawable3);
-        when(mMockIconInjector.getIcon(4)).thenReturn(mMockDrawable4);
+        when(mMockIconInjector.getIcon(false /* showX */, 0)).thenReturn(mMockDrawable0);
+        when(mMockIconInjector.getIcon(false /* showX */, 1)).thenReturn(mMockDrawable1);
+        when(mMockIconInjector.getIcon(false /* showX */, 2)).thenReturn(mMockDrawable2);
+        when(mMockIconInjector.getIcon(false /* showX */, 3)).thenReturn(mMockDrawable3);
+        when(mMockIconInjector.getIcon(false /* showX */, 4)).thenReturn(mMockDrawable4);
+
+        when(mMockIconInjector.getIcon(true /* showX */, 0))
+                .thenReturn(mMockShowXDrawable0);
+        when(mMockIconInjector.getIcon(true /* showX */, 1))
+                .thenReturn(mMockShowXDrawable1);
+        when(mMockIconInjector.getIcon(true /* showX */, 2))
+                .thenReturn(mMockShowXDrawable2);
+        when(mMockIconInjector.getIcon(true /* showX */, 3))
+                .thenReturn(mMockShowXDrawable3);
+        when(mMockIconInjector.getIcon(true /* showX */, 4))
+                .thenReturn(mMockShowXDrawable4);
     }
 
     @Test
@@ -155,6 +179,70 @@
     }
 
     @Test
+    public void levelChanged_notDefaultWifiRefresh_shouldUpdateLevelIcon() {
+        final List<Drawable> iconList = new ArrayList<>();
+        final ConnectedInfo mockConnectedInfo = mock(ConnectedInfo.class);
+        mockConnectedInfo.isDefaultNetwork = false;
+        when(mMockWifiEntry.getConnectedInfo()).thenReturn(mockConnectedInfo);
+        final WifiEntryPreference pref =
+                new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
+
+        when(mMockWifiEntry.getLevel()).thenReturn(0);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(1);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(2);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(3);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(4);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(-1);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+
+        assertThat(iconList).containsExactly(mMockShowXDrawable0, mMockShowXDrawable1,
+                mMockShowXDrawable2, mMockShowXDrawable3, mMockShowXDrawable4, null);
+    }
+
+    @Test
+    public void levelChanged_notValidatedWifiRefresh_shouldUpdateLevelIcon() {
+        final List<Drawable> iconList = new ArrayList<>();
+        final ConnectedInfo mockConnectedInfo = mock(ConnectedInfo.class);
+        mockConnectedInfo.isValidated = false;
+        when(mMockWifiEntry.getConnectedInfo()).thenReturn(mockConnectedInfo);
+        final WifiEntryPreference pref =
+                new WifiEntryPreference(mContext, mMockWifiEntry, mMockIconInjector);
+
+        when(mMockWifiEntry.getLevel()).thenReturn(0);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(1);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(2);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(3);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(4);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+        when(mMockWifiEntry.getLevel()).thenReturn(-1);
+        pref.refresh();
+        iconList.add(pref.getIcon());
+
+        assertThat(iconList).containsExactly(mMockShowXDrawable0, mMockShowXDrawable1,
+                mMockShowXDrawable2, mMockShowXDrawable3, mMockShowXDrawable4, null);
+    }
+
+    @Test
     public void notNull_whenGetHelpUriString_shouldSetImageButtonVisible() {
         when(mMockWifiEntry.getHelpUriString()).thenReturn(FAKE_URI_STRING);
         final WifiEntryPreference pref =
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 66be8dd..a293148 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -860,9 +860,6 @@
         p.end(intentFirewallToken);
 
         dumpSetting(s, p,
-                Settings.Global.JOB_SCHEDULER_CONSTANTS,
-                GlobalSettingsProto.JOB_SCHEDULER_CONSTANTS);
-        dumpSetting(s, p,
                 Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
                 GlobalSettingsProto.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS);
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 0ac3355..f219aec 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -309,7 +309,6 @@
                     Settings.Global.INSTANT_APP_DEXOPT_ENABLED,
                     Settings.Global.INTENT_FIREWALL_UPDATE_CONTENT_URL,
                     Settings.Global.INTENT_FIREWALL_UPDATE_METADATA_URL,
-                    Settings.Global.JOB_SCHEDULER_CONSTANTS,
                     Settings.Global.JOB_SCHEDULER_QUOTA_CONTROLLER_CONSTANTS,
                     Settings.Global.KEEP_PROFILE_IN_BACKGROUND,
                     Settings.Global.KERNEL_CPU_THREAD_READER,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 2fbd9ba..0f2e25c 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -174,6 +174,9 @@
     kotlincflags: ["-Xjvm-default=enable"],
 
     dxflags: ["--multi-dex"],
-    required: ["privapp_whitelist_com.android.systemui"],
+    required: [
+        "privapp_whitelist_com.android.systemui",
+        "checked-wm_shell_protolog.json",
+    ],
 
 }
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index af008b9..7f4f580 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -530,20 +530,6 @@
             androidprv:alwaysFocusable="true"
             android:excludeFromRecents="true" />
 
-        <activity
-            android:name=".pip.phone.PipMenuActivity"
-            android:permission="com.android.systemui.permission.SELF"
-            android:theme="@style/PipPhoneOverlayControlTheme"
-            android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
-            android:excludeFromRecents="true"
-            android:exported="false"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:stateNotNeeded="true"
-            android:taskAffinity=""
-            android:launchMode="singleTop"
-            androidprv:alwaysFocusable="true" />
-
         <!-- started from SliceProvider -->
         <activity android:name=".SlicePermissionActivity"
             android:theme="@style/Theme.SystemUI.Dialog.Alert"
diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md
index 68b9553..148fabb 100644
--- a/packages/SystemUI/README.md
+++ b/packages/SystemUI/README.md
@@ -88,11 +88,6 @@
 Registers all the callbacks/listeners required to show the Volume dialog when
 it should be shown.
 
-### [com.android.systemui.stackdivider.Divider](/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java)
-
-Shows the drag handle for the divider between two apps when in split screen
-mode.
-
 ### [com.android.systemui.status.phone.StatusBar](/packages/SystemUI/src/com/android/systemui/status/phone/StatusBar.java)
 
 This shows the UI for the status bar and the notification shade it contains.
@@ -153,6 +148,10 @@
 
 Biometric UI.
 
+### [com.android.systemui.wmshell.WMShell](/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java)
+
+Delegates SysUI events to WM Shell controllers.
+
 ---
 
  * [Plugins](/packages/SystemUI/docs/plugins.md)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 9d52098..63f8b1f 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -30,7 +30,7 @@
  */
 @ProvidesInterface(version = FalsingManager.VERSION)
 public interface FalsingManager {
-    int VERSION = 4;
+    int VERSION = 5;
 
     void onSuccessfulUnlock();
 
@@ -42,7 +42,8 @@
 
     boolean isUnlockingDisabled();
 
-    boolean isFalseTouch();
+    /** Returns true if the gesture should be rejected. */
+    boolean isFalseTouch(int interactionType);
 
     void onNotificatonStopDraggingDown();
 
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
index 02c4c5e..4b6efa9 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/NotificationSwipeActionHelper.java
@@ -14,16 +14,16 @@
 
 package com.android.systemui.plugins.statusbar;
 
-import com.android.systemui.plugins.annotations.DependsOn;
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
-
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
+import com.android.systemui.plugins.annotations.DependsOn;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
+
 @ProvidesInterface(version = NotificationSwipeActionHelper.VERSION)
 @DependsOn(target = SnoozeOption.class)
 public interface NotificationSwipeActionHelper {
@@ -52,7 +52,8 @@
 
     public boolean isDismissGesture(MotionEvent ev);
 
-    public boolean isFalseGesture(MotionEvent ev);
+    /** Returns true if the gesture should be rejected. */
+    boolean isFalseGesture();
 
     public boolean swipedFarEnough(float translation, float viewSize);
 
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index df66bf5..6c06b0a 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -41,7 +41,12 @@
     public <init>(android.content.Context);
 }
 
+# Keep the wm shell lib
 -keep class com.android.wm.shell.*
+# Keep the protolog group methods that are called by the generated code
+-keepclassmembers class com.android.wm.shell.protolog.ShellProtoLogGroup {
+    *;
+}
 
 -keep class com.android.systemui.dagger.GlobalRootComponent { *; }
 -keep class com.android.systemui.dagger.GlobalRootComponent$SysUIComponentImpl { *; }
diff --git a/packages/SystemUI/res-keyguard/drawable/face_auth_wallpaper.png b/packages/SystemUI/res-keyguard/drawable/face_auth_wallpaper.png
new file mode 100644
index 0000000..b907f4e
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/face_auth_wallpaper.png
Binary files differ
diff --git a/packages/SystemUI/res/layout/notification_conversation_info.xml b/packages/SystemUI/res/layout/notification_conversation_info.xml
index 2c4b937..fd89c0b 100644
--- a/packages/SystemUI/res/layout/notification_conversation_info.xml
+++ b/packages/SystemUI/res/layout/notification_conversation_info.xml
@@ -210,7 +210,7 @@
                     android:clickable="false"
                     android:focusable="false"
                     android:ellipsize="end"
-                    android:maxLines="3"
+                    android:maxLines="4"
                     android:textAppearance="@style/TextAppearance.NotificationImportanceDetail"/>
             </com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
 
diff --git a/packages/SystemUI/res/layout/partial_conversation_info.xml b/packages/SystemUI/res/layout/partial_conversation_info.xml
index 0795170..c353d08 100644
--- a/packages/SystemUI/res/layout/partial_conversation_info.xml
+++ b/packages/SystemUI/res/layout/partial_conversation_info.xml
@@ -128,6 +128,7 @@
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
                     android:layout_centerVertical="true"
+                    android:textDirection="locale"
                     style="@style/TextAppearance.NotificationImportanceChannelGroup" />
             </LinearLayout>
         </com.android.systemui.statusbar.notification.row.ButtonLinearLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index bac915d..316fa8a 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Hervat"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Kanselleer"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Deel"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Vee uit"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Skermopname is gekanselleer"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Skermopname is gestoor, tik om te sien"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Skermopname is uitgevee"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Kon nie skermopname uitvee nie"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Kon nie toestemmings kry nie"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Kon nie skermopname begin nie"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 43ab1d2..e2a4dc6 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"ከቆመበት ቀጥል"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"ይቅር"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"አጋራ"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"ሰርዝ"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"የማያ ገጽ ቀረጻ ተሰርዟል"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"የማያ ገጽ ቀረጻ ተቀምጧል፣ ለመመልከት መታ ያድርጉ"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"የማያ ገጽ ቀረጻ ተሰርዟል"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"የማያ ገጽ ቀረጻን መሰረዝ ላይ ስህተት"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"ፈቃዶችን ማግኘት አልተቻለም"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"የማያ ገጽ ቀረጻን መጀመር ላይ ስህተት"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index c68d088..b33c6c5 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"استئناف"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"إلغاء"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"مشاركة"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"حذف"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"تمّ إلغاء تسجيل الشاشة."</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"تمّ حفظ تسجيل الشاشة، انقر لعرضه."</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"تمّ حذف تسجيل الشاشة."</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"حدث خطأ أثناء حذف تسجيل الشاشة."</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"تعذّر الحصول على أذونات."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"حدث خطأ في بدء تسجيل الشاشة"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index d604fd4..5a3c659 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"ৰখোৱাৰ পৰা পুনৰ আৰম্ভ কৰক"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"বাতিল কৰক"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"শ্বেয়াৰ কৰক"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"মচক"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"স্ক্রীণ ৰেকৰ্ড কৰাটো বাতিল কৰা হ’ল"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"স্ক্রীণ ৰেকৰ্ডিং ছেভ কৰা হ’ল, চাবলৈ টিপক"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"স্ক্রীণ ৰেকৰ্ডিং মচা হ’ল"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"স্ক্রীণ ৰেকৰ্ডিং মচি থাকোঁতে কিবা আসোঁৱাহ হ’ল"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"অনুমতি পাব পৰা নগ\'ল"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"স্ক্রীন ৰেকৰ্ড কৰা আৰম্ভ কৰোঁতে আসোঁৱাহ হৈছে"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index b1df252..4aa4bda 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Davam edin"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Ləğv edin"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Paylaşın"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Silin"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Ekranın video çəkimi ləğv edildi"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Ekranın video çəkimi yadda saxlanıldı. Baxmaq üçün klikləyin"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Ekranın video çəkimi silindi"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Ekranın video çəkiminin silinməsi zamanı xəta baş verdi"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"İcazələr əldə edilmədi"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Ekranın yazılması ilə bağlı xəta"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index f3c4c6b..a9725f3 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Nastavi"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Otkaži"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Deli"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Izbriši"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Snimanje ekrana je otkazano"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Snimak ekrana je sačuvan, dodirnite da biste pregledali"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Snimak ekrana je izbrisan"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Došlo je do problema pri brisanju snimka ekrana"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Preuzimanje dozvola nije uspelo"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Greška pri pokretanju snimanja ekrana"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 9c0eeb2..b4c5eba 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Узнавіць"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Скасаваць"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Абагуліць"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Выдаліць"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Запіс экрана скасаваны"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Запіс экрана захаваны. Націсніце, каб прагледзець"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Запіс экрана выдалены"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Памылка выдалення запісу экрана"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Не ўдалося атрымаць дазволы"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Памылка пачатку запісу экрана"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 3bf12ad..52e8286 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Възобновяване"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Отказ"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Споделяне"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Изтриване"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Записването на екрана е анулирано"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Записът на екрана е запазен. Докоснете, за да го видите"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Записът на екрана е изтрит"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"При изтриването на записа на екрана възникна грешка"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Извличането на разрешенията не бе успешно."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"При стартирането на записа на екрана възникна грешка"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 5b7c953..d0de9cf 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"আবার চালু করুন"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"বাতিল করুন"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"শেয়ার করুন"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"মুছুন"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"স্ক্রিন রেকর্ডিং বাতিল করা হয়েছে"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"স্ক্রিন রেকর্ডিং সেভ করা হয়েছে, দেখতে ট্যাপ করুন"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"স্ক্রিন রেকর্ডিং মুছে ফেলা হয়েছে"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"স্ক্রিন রেকডিং মুছে ফেলার সময় সমস্যা হয়েছে"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"অনুমতি পাওয়া যায়নি"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"স্ক্রিন রেকর্ডিং শুরু করার সময় সমস্যা হয়েছে"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 31f88c8f..57b7945 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Nastavi"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Otkaži"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Dijeli"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Izbriši"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Snimanje ekrana je otkazano"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Snimak ekrana je sačuvan. Dodirnite za prikaz."</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Snimak ekrana je izbrisan"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Greška prilikom brisanja snimka ekrana"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Dobijanje odobrenja nije uspjelo"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Greška pri pokretanju snimanja ekrana"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 753e25f..0489d18 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Reprèn"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Cancel·la"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Comparteix"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Suprimeix"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"S\'ha cancel·lat la gravació de la pantalla"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"S\'ha desat la gravació de la pantalla; toca per mostrar"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"S\'ha suprimit la gravació de la pantalla"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"S\'ha produït un error en suprimir la gravació de la pantalla"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"No s\'han pogut obtenir els permisos"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"S\'ha produït un error en iniciar la gravació de pantalla"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 039500d..121a81d 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Obnovit"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Zrušit"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Sdílet"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Smazat"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Nahrávání obrazovky bylo zrušeno"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Záznam obrazovky byl uložen, zobrazíte jej klepnutím"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Záznam obrazovky byl smazán"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Při mazání záznamu obrazovky došlo k chybě"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Nepodařilo se načíst oprávnění"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Při spouštění nahrávání obrazovky došlo k chybě"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 0c8a5b7..8863695 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Genoptag"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Annuller"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Del"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Slet"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Skærmoptagelsen er annulleret"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Skærmoptagelsen er gemt. Tryk for at se den."</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Skærmoptagelsen er slettet"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Der opstod en fejl ved sletning af skærmoptagelsen"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Det lykkedes ikke et hente tilladelserne"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Skærmoptagelsen kunne ikke startes"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 1c4c7ab..8d990c6 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Fortsetzen"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Abbrechen"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Teilen"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Löschen"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Bildschirmaufzeichnung abgebrochen"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Bildschirmaufzeichnung gespeichert, zum Ansehen tippen"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Bildschirmaufzeichnung gelöscht"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Fehler beim Löschen der Bildschirmaufzeichnung"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Berechtigungen nicht erhalten"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Fehler beim Start der Bildschirmaufzeichnung"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 07ff807..4384749 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Συνέχιση"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Ακύρωση"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Κοινοποίηση"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Διαγραφή"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Η εγγραφή οθόνης ακυρώθηκε"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Η εγγραφή οθόνης αποθηκεύτηκε. Πατήστε για προβολή."</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Η εγγραφή οθόνης διαγράφηκε"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Παρουσιάστηκε σφάλμα κατά τη διαγραφή της εγγραφής οθόνης"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Η λήψη αδειών απέτυχε"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Σφάλμα κατά την έναρξη της εγγραφής οθόνης"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 0e22b58..8a7963a 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Resume"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Cancel"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Share"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Delete"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Screen recording cancelled"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Screen recording saved, tap to view"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Screen recording deleted"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Error deleting screen recording"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Failed to get permissions"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index acf087d..7b2b25a 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Resume"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Cancel"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Share"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Delete"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Screen recording cancelled"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Screen recording saved, tap to view"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Screen recording deleted"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Error deleting screen recording"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Failed to get permissions"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 0e22b58..8a7963a 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Resume"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Cancel"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Share"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Delete"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Screen recording cancelled"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Screen recording saved, tap to view"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Screen recording deleted"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Error deleting screen recording"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Failed to get permissions"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 0e22b58..8a7963a 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Resume"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Cancel"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Share"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Delete"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Screen recording cancelled"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Screen recording saved, tap to view"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Screen recording deleted"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Error deleting screen recording"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Failed to get permissions"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Error starting screen recording"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 4f4238a..15738f2 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‏‎‎‏‏‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‎‏‏‏‎‏‏‏‎Resume‎‏‎‎‏‎"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‎‏‎‎‎‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‏‏‎‎‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‎‎Cancel‎‏‎‎‏‎"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‎‏‎‎‏‎‎‏‎‏‎‏‏‏‎‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‎‎Share‎‏‎‎‏‎"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‏‎‎‎‏‏‎‎‏‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎Delete‎‏‎‎‏‎"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‎‎‏‏‏‎‏‎‏‎‎‏‎‎‏‎‏‏‎‎‏‏‎‏‎‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‏‏‎‏‎Screen recording canceled‎‏‎‎‏‎"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‎‏‏‏‎‏‎‏‎‏‏‏‏‎‎‏‏‎‏‎‏‎‎‏‎‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‎‎‏‎‎Screen recording saved, tap to view‎‏‎‎‏‎"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎Screen recording deleted‎‏‎‎‏‎"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‏‏‎‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‎‏‎‏‏‎‏‎‏‎‎‎Error deleting screen recording‎‏‎‎‏‎"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‏‎‎‏‎‏‏‎‎Failed to get permissions‎‏‎‎‏‎"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‎‎‎‎‎‎‎Error starting screen recording‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 38fa528..aa81ab8 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Reanudar"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Cancelar"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Compartir"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Borrar"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Se canceló la grabación de pantalla"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Se guardó la grabación de pantalla; presiona para verla"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Se borró la grabación de pantalla"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Error al borrar la grabación de pantalla"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Error al obtener permisos"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Error al iniciar la grabación de pantalla"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 10aa6ca..8638570 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Seguir"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Cancelar"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Compartir"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Eliminar"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Se ha cancelado la grabación de la pantalla"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Se ha guardado la grabación de la pantalla; toca para verla"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Se ha eliminado la grabación de la pantalla"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"No se ha podido eliminar la grabación de la pantalla"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"No se han podido obtener los permisos"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"No se ha podido empezar a grabar la pantalla"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 9122ce5..f5ffad2 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Jätka"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Tühista"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Jaga"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Kustuta"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Ekraanikuva salvestamine on tühistatud"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Ekraanikuva salvestis on salvestatud, puudutage vaatamiseks"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Ekraanikuva salvestis on kustutatud"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Viga ekraanikuva salvestise kustutamisel"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Lubade hankimine ebaõnnestus"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Viga ekraanikuva salvestamise alustamisel"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 38b2103d..1d5ac1a 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Berrekin"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Utzi"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Partekatu"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Ezabatu"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Utzi zaio pantaila grabatzeari"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Gorde da pantailaren grabaketa; sakatu ikusteko"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Ezabatu da pantailaren grabaketa"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Errore bat gertatu da pantailaren grabaketa ezabatzean"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Ezin izan dira lortu baimenak"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Errore bat gertatu da pantaila grabatzen hastean"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index d39d0c3..55d8ab2 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"ازسرگیری"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"لغو"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"هم‌رسانی"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"حذف"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"ضبط صفحه‌نمایش لغو شد"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"ضبط صفحه‌نمایش ذخیره شد، برای مشاهده ضربه بزنید"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"فایل ضبط صفحه‌نمایش حذف شد"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"خطا در حذف فایل ضبط صفحه‌نمایش"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"مجوزها دریافت نشدند"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"خطا هنگام شروع ضبط صفحه‌نمایش"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 52e5e8c..f30c44b 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Jatka"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Peruuta"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Jaa"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Poista"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Näytön tallennus peruutettu"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Näyttötallenne tallennettu, katso napauttamalla"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Näyttötallenne poistettu"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Virhe poistettaessa näyttötallennetta"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Käyttöoikeuksien hakeminen epäonnistui."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Virhe näytön tallennuksen aloituksessa"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 4797f32..f6196dd63 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Reprendre"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Annuler"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Partager"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Supprimer"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"L\'enregistrement d\'écran a été annulé"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"L\'enregistrement d\'écran est terminé. Touchez ici pour l\'afficher."</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"L\'enregistrement d\'écran a été supprimé"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Une erreur s\'est produite lors de la suppression de l\'enregistrement d\'écran"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Impossible d\'obtenir les autorisations"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Une erreur s\'est produite lors du démarrage de l\'enregistrement d\'écran"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index e5df728..0146ce4 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Reprendre"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Annuler"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Partager"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Supprimer"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Enregistrement de l\'écran annulé"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Enregistrement de l\'écran enregistré. Appuyez pour afficher"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Enregistrement de l\'écran supprimé"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Erreur lors de la suppression de l\'enregistrement de l\'écran"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Échec d\'obtention des autorisations"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Erreur lors du démarrage de l\'enregistrement de l\'écran"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 246fefd..fd4d3c6 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Retomar"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Cancelar"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Compartir"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Eliminar"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Cancelouse a gravación de pantalla"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Gardouse a gravación de pantalla; toca esta notificación para visualizala"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Eliminouse a gravación de pantalla"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Produciuse un erro ao eliminar a gravación de pantalla"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Produciuse un erro ao obter os permisos"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Produciuse un erro ao iniciar a gravación da pantalla"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index beaacaa..46f7a2b 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"ફરી શરૂ કરો"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"રદ કરો"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"શેર કરો"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"ડિલીટ કરો"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"સ્ક્રીન રેકોર્ડિંગ રદ કર્યું"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"સ્ક્રીન રેકોર્ડિંગ સાચવ્યું, જોવા માટે ટૅપ કરો"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"સ્ક્રીન રેકોર્ડિંગ ડિલીટ કર્યું"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"સ્ક્રીન રેકોર્ડિંગ ડિલીટ કરવામાં ભૂલ આવી"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"પરવાનગીઓ મેળવવામાં નિષ્ફળ રહ્યાં"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"સ્ક્રીનને રેકૉર્ડ કરવાનું શરૂ કરવામાં ભૂલ"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index de4fc48..e4a6ed7 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"फिर से शुरू करें"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"रद्द करें"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"शेयर करें"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"मिटाएं"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"स्क्रीन रिकॉर्डिंग रद्द कर दी गई"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"स्क्रीन रिकॉर्डिंग सेव की गई, देखने के लिए टैप करें"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"स्क्रीन रिकॉर्डिंग मिटा दी गई"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"स्क्रीन रिकॉर्डिंग मिटाने में गड़बड़ी हुई"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"मंज़ूरी नहीं मिल सकी"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"स्क्रीन को रिकॉर्ड करने में गड़बड़ी आ रही है"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 7bf0b93..a27b066 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Nastavi"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Odustani"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Dijeli"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Izbriši"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Snimanje zaslona otkazano"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Snimanje zaslona spremljeno je, dodirnite da biste ga pregledali"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Snimanje zaslona izbrisano"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Pogreška prilikom brisanja snimanja zaslona"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Dohvaćanje dopuštenja nije uspjelo"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Pogreška prilikom pokretanja snimanja zaslona"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 875a05f..0d1f50a 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Folytatás"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Mégse"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Megosztás"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Törlés"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"A képernyő rögzítése megszakítva"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Képernyőfelvétel mentve, koppintson a megtekintéshez"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"A képernyőről készült felvétel törölve"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Hiba történt a képernyőről készült felvétel törlésekor"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Nincs engedély"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Hiba a képernyőrögzítés indításakor"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 4cc406b..f9a10fa 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Վերսկսել"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Չեղարկել"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Կիսվել"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Ջնջել"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Էկրանի տեսագրումը չեղարկվեց"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Էկրանի տեսագրությունը պահվեց։ Հպեք՝ դիտելու համար:"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Էկրանի տեսագրությունը ջնջվեց"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Չհաջողվեց ջնջել տեսագրությունը"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Չհաջողվեց ստանալ անհրաժեշտ թույլտվությունները"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Չհաջողվեց սկսել տեսագրումը"</string>
@@ -337,7 +335,7 @@
     <string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"Էկրանը կողպված է ուղղաձիգ դիրքավորմամբ:"</string>
     <string name="accessibility_rotation_lock_off_changed" msgid="5772498370935088261">"Էկրանն այժմ ավտոմատ կպտտվի:"</string>
     <string name="accessibility_rotation_lock_on_landscape_changed" msgid="5785739044300729592">"Էկրանն այժմ կողպված է հորիզոնական դիրքում:"</string>
-    <string name="accessibility_rotation_lock_on_portrait_changed" msgid="5580170829728987989">"Էկրանն այժմ կողպված է ուղղահայաց դիրքում:"</string>
+    <string name="accessibility_rotation_lock_on_portrait_changed" msgid="5580170829728987989">"Էկրանն այժմ կողպված է ուղղաձիգ դիրքում:"</string>
     <string name="dessert_case" msgid="9104973640704357717">"Dessert Case"</string>
     <string name="start_dreams" msgid="9131802557946276718">"Էկրանապահ"</string>
     <string name="ethernet_label" msgid="2203544727007463351">"Ethernet"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 5f7dce4..f65be90 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Lanjutkan"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Batal"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Bagikan"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Hapus"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Rekaman layar dibatalkan"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Rekaman layar disimpan, ketuk untuk melihat"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Rekaman layar dihapus"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Error saat menghapus rekaman layar"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Gagal mendapatkan izin"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Terjadi error saat memulai perekaman layar"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index b5f6e57..66c9147 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Halda áfram"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Hætta við"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Deila"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Eyða"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Hætt við skjáupptöku"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Skjáupptaka vistuð, ýttu til að skoða"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Skjáupptöku eytt"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Villa við að eyða skjáupptöku"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Ekki tókst að fá heimildir"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Villa við að hefja upptöku skjás"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index f4453b7..b0e64a2 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Riprendi"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Annulla"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Condividi"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"CANC"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Registrazione dello schermo annullata"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Registrazione dello schermo salvata. Tocca per visualizzarla."</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Registrazione dello schermo eliminata"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Errore durante l\'eliminazione della registrazione dello schermo"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Impossibile ottenere le autorizzazioni"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Errore durante l\'avvio della registrazione dello schermo"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 161fedc..92fa09b 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"המשך"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"ביטול"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"שיתוף"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"מחיקה"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"הקלטת המסך בוטלה"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"הקלטת המסך נשמרה, יש להקיש כדי להציג"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"הקלטת המסך נמחקה"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"שגיאה במחיקת הקלטת המסך"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"קבלת ההרשאות נכשלה"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"שגיאה בהפעלה של הקלטת המסך"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index fa9c040..e427c8f 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"再開"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"キャンセル"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"共有"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"削除"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"画面の録画をキャンセルしました"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"画面の録画を保存しました。タップで表示できます"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"画面の録画を削除しました"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"画面の録画の削除中にエラーが発生しました"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"権限を取得できませんでした"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"画面の録画中にエラーが発生しました"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index a3ddf70..b86bf8c 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"გაგრძელება"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"გაუქმება"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"გაზიარება"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"წაშლა"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"ეკრანის ჩაწერა გაუქმდა"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"ეკრანის ჩანაწერი შენახულია, შეეხეთ სანახავად"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"ეკრანის ჩანაწერი წაიშალა"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"ეკრანის ჩანაწერის წაშლისას წარმოიშვა შეცდომა"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"ნებართვების მიღება ვერ მოხერხდა"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ეკრანის ჩაწერის დაწყებისას წარმოიქმნა შეცდომა"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 0d3f7f9..1adaf2b 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Жалғастыру"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Бас тарту"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Бөлісу"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Жою"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Экранды бейнеге жазудан бас тартылды"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Экран бейне жазбасы сақталды, көру үшін түртіңіз"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Экран бейне жазбасы жойылды"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Экран бейне жазбасын жою кезінде қате кетті"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Рұқсаттар алынбады"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Экрандағы бейнені жазу кезінде қате шықты."</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 5abd740..961a37b 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"បន្ត"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"បោះបង់"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"ចែករំលែក"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"លុប"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"បាន​បោះបង់​ការថត​សកម្មភាព​អេក្រង់"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"បានរក្សាទុក​ការថត​សកម្មភាព​អេក្រង់។ សូមចុច​ដើម្បី​មើល"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"បានលុប​ការថត​សកម្មភាព​អេក្រង់"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"មានបញ្ហា​ក្នុងការ​លុបការថត​សកម្មភាព​អេក្រង់"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"មិនអាច​ទទួលបាន​ការអនុញ្ញាត​ទេ"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"មានបញ្ហា​ក្នុងការ​ចាប់ផ្ដើម​ថត​អេក្រង់"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 47f2385..7ec6a9f 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"ಮುಂದುವರಿಸಿ"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"ರದ್ದುಮಾಡಿ"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"ಹಂಚಿಕೊಳ್ಳಿ"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"ಅಳಿಸಿ"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಅನ್ನು ರದ್ದುಮಾಡಲಾಗಿದೆ"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಅನ್ನು ಉಳಿಸಲಾಗಿದೆ, ವೀಕ್ಷಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಅಳಿಸಲಾಗಿದೆ"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಅಳಿಸುವಾಗ ದೋಷ ಕಂಡುಬಂದಿದೆ"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"ಅನುಮತಿಗಳನ್ನು ಪಡೆಯುವಲ್ಲಿ ವಿಫಲವಾಗಿದೆ"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಪ್ರಾರಂಭಿಸುವಾಗ ದೋಷ ಕಂಡುಬಂದಿದೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index beb7eda..840a98e 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"재개"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"취소"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"공유"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"삭제"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"화면 녹화가 취소되었습니다."</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"화면 녹화본이 저장되었습니다. 확인하려면 탭하세요."</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"화면 녹화가 삭제되었습니다."</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"화면 녹화는 삭제하는 중에 오류가 발생했습니다."</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"권한을 확보하지 못했습니다."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"화면 녹화 시작 중 오류 발생"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 4fbb09e..14ee566 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Улантуу"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Жокко чыгаруу"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Бөлүшүү"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Ооба"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Экранды жаздыруу жокко чыгарылды"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Экранды жаздыруу сакталды, көрүү үчүн таптап коюңуз"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Экранды жаздыруу өчүрүлдү"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Экранды жаздырууну өчүрүүдө ката кетти"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Уруксаттар алынбай калды"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Экранды жаздырууну баштоодо ката кетти"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 6d7feab..553d0da 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"ສືບຕໍ່"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"ຍົກເລີກ"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"ແບ່ງປັນ"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"ລຶບ"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"ຍົກເລີກການບັນທຶກໜ້າຈໍແລ້ວ"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"ຈັດເກັບການບັນທຶກໜ້າຈໍ, ແຕະເພື່ອເບິ່ງ"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"ລຶບການບັນທຶກໜ້າຈໍອອກແລ້ວ"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"ເກີດຄວາມຜິດພາດໃນການລຶບການບັນທຶກໜ້າຈໍ"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"ໂຫຼດສິດອະນຸຍາດບໍ່ສຳເລັດ"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ເກີດຄວາມຜິດພາດໃນການບັນທຶກໜ້າຈໍ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 21217a0..28d6e6b 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Tęsti"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Atšaukti"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Bendrinti"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Ištrinti"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Ekrano įrašymas atšauktas"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Ekrano įrašas išsaugotas, palieskite ir peržiūrėkite"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Ekrano įrašas ištrintas"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Ištrinant ekrano įrašą įvyko klaida"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Nepavyko gauti leidimų"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Pradedant ekrano vaizdo įrašymą iškilo problema"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 4945dfa..223a57f 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Atsākt"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Atcelt"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Kopīgot"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Dzēst"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Ekrāna ierakstīšana ir atcelta."</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Ekrāna ieraksts ir saglabāts. Pieskarieties, lai to skatītu."</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Ekrāna ieraksts ir izdzēsts."</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Dzēšot ekrāna ierakstu, radās kļūda."</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Neizdevās iegūt atļaujas."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Sākot ierakstīt ekrāna saturu, radās kļūda."</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 40d4698..a591031 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Продолжи"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Откажи"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Сподели"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Избриши"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Снимањето екран е откажано"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Снимката од екранот е зачувана, допрете за да ја видите"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Снимката од екранот е избришана"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Грешка при бришењето на снимката од екранот"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Не успеаја да се добијат дозволи"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Грешка при почетокот на снимањето на екранот"</string>
@@ -667,7 +665,7 @@
     <string name="tuner_warning_title" msgid="7721976098452135267">"Забава за некои, но не за сите"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"Адаптерот на УИ на системот ви дава дополнителни начини за дотерување и приспособување на корисничкиот интерфејс на Android. Овие експериментални функции можеби ќе се изменат, расипат или ќе исчезнат во следните изданија. Продолжете со претпазливост."</string>
     <string name="tuner_persistent_warning" msgid="230466285569307806">"Овие експериментални функции можеби ќе се изменат, расипат или ќе исчезнат во следните изданија. Продолжете со претпазливост."</string>
-    <string name="got_it" msgid="477119182261892069">"Разбрав"</string>
+    <string name="got_it" msgid="477119182261892069">"Сфатив"</string>
     <string name="tuner_toast" msgid="3812684836514766951">"Честито! Го додадовте Адаптерот на УИ на системот на Поставки"</string>
     <string name="remove_from_settings" msgid="633775561782209994">"Отстрани од поставки"</string>
     <string name="remove_from_settings_prompt" msgid="551565437265615426">"Да се отстрани Адаптерот на УИ на системот од Поставки и да престанат да се користат сите негови функции?"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index c3bd65e..47d220d 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"പുനരാരംഭിക്കുക"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"റദ്ദാക്കുക"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"പങ്കിടുക"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"ഇല്ലാതാക്കുക"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"സ്ക്രീൻ റെക്കോർഡിംഗ് റദ്ദാക്കി"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"സ്ക്രീൻ റെക്കോർഡിംഗ് സംരക്ഷിച്ചു, കാണാൻ ടാപ്പ് ചെയ്യുക"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"സ്ക്രീൻ റെക്കോർഡിംഗ് ഇല്ലാതാക്കി"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"സ്ക്രീൻ റെക്കോർഡിംഗ് ഇല്ലാതാക്കുന്നതിൽ പിശക്"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"അനുമതികൾ ലഭിച്ചില്ല"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"സ്ക്രീൻ റെക്കോർഡിംഗ് ആരംഭിക്കുന്നതിൽ പിശക്"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 5f08f05..202f292 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Үргэлжлүүлэх"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Цуцлах"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Хуваалцах"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Устгах"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Дэлгэцийн бичлэгийг цуцалсан"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Дэлгэцийн бичлэгийг хадгалсан. Харахын тулд товшино уу"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Дэлгэцийн бичлэгийг устгасан"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Дэлгэцийн бичлэгийг устгахад алдаа гарлаа"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Зөвшөөрөл авч чадсангүй"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Дэлгэцийн бичлэгийг эхлүүлэхэд алдаа гарлаа"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 6d525df..e412072 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"पुन्हा सुरू करा"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"रद्द करा"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"शेअर करा"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"हटवा"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"स्क्रीन रेकॉर्डिंग रद्द केले"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"स्क्रीन रेकॉर्डिंग सेव्ह केली, पाहण्यासाठी टॅप करा"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"स्क्रीन रेकॉर्डिंग हटवले"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"स्क्रीन रेकॉर्डिंग हटवताना एरर आली"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"परवानग्या मिळवता आल्या नाहीत"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"स्क्रीन रेकॉर्डिंग सुरू करताना एरर आली"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 4f832e4..d01ab8c 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Sambung semula"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Batal"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Kongsi"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Padam"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Rakaman skrin dibatalkan"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Rakaman skrin disimpan, ketik untuk melihat"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Rakaman skrin dipadamkan"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Ralat semasa memadamkan rakaman skrin"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Gagal mendapatkan kebenaran"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Ralat semasa memulakan rakaman skrin"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 150ed94e..83272f2 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"ဆက်လုပ်ရန်"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"မလုပ်တော့"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"မျှဝေရန်"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"ဖျက်ရန်"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"ဖန်သားပြင် ရိုက်ကူးမှု ပယ်ဖျက်လိုက်ပါပြီ"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"ဖန်သားပြင် ရိုက်ကူးမှု သိမ်းထားသည်၊ ကြည့်ရန် တို့ပါ"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"ဖန်သားပြင် ရိုက်ကူးမှု ဖျက်ပြီးပါပြီ"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"ဖန်သားပြင် ရိုက်ကူးမှု ဖျက်ရာတွင် အမှားအယွင်းရှိနေသည်"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"ခွင့်ပြုချက် မရယူနိုင်ပါ"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ဖန်သားပြင် ရိုက်ကူးမှု စတင်ရာတွင် အမှားအယွင်းရှိနေသည်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 849c2e9..01859ef 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Gjenoppta"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Avbryt"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Del"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Slett"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Skjermopptak er avbrutt"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Skjermopptaket er lagret. Trykk for å se det"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Skjermopptaket er slettet"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Feil ved sletting av skjermopptaket"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Kunne ikke få tillatelser"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Feil ved start av skjermopptaket"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 2ac442e..e548a96 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"जारी राख्नुहोस्"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"रद्द गर्नुहोस्"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"सेयर गर्नुहोस्"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"मेट्नुहोस्"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"स्क्रिन रेकर्ड गर्ने कार्य रद्द गरियो"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"स्क्रिन रेकर्डिङ सुरक्षित गरियो, हेर्न ट्याप गर्नुहोस्‌"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"स्क्रिनको रेकर्डिङ मेटाइयो"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"स्क्रिनको रेकर्डिङ मेट्ने क्रममा त्रुटि"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"अनुमति प्राप्त गर्न सकिएन"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"स्क्रिन रेकर्ड गर्न थाल्ने क्रममा त्रुटि भयो"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index da0f427..fa028c3 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Hervatten"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Annuleren"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Delen"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Verwijderen"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Schermopname geannuleerd"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Schermopname opgeslagen, tik om te bekijken"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Schermopname verwijderd"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Fout bij verwijderen van schermopname"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Kan rechten niet ophalen"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Fout bij starten van schermopname"</string>
@@ -354,7 +352,7 @@
     <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string>
     <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string>
     <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Invoer"</string>
-    <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Gehoorapparaten"</string>
+    <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="3003338571871392293">"Hoortoestellen"</string>
     <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Inschakelen..."</string>
     <string name="quick_settings_brightness_label" msgid="680259653088849563">"Helderheid"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatisch draaien"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 029aa69..4c6ef48 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"ବାତିଲ୍‌ କରନ୍ତୁ"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"ସେୟାର୍‍ କରନ୍ତୁ"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"ଡିଲିଟ୍ କରନ୍ତୁ"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"ସ୍କ୍ରିନ୍‍ ରେକର୍ଡିଂ ବାତିଲ୍‌ କରିଦିଆଯାଇଛି"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"ସ୍କ୍ରିନ୍‍ ରେକର୍ଡିଂ ସେଭ୍‍ ହୋଇଛି, ଦେଖିବାକୁ ଟାପ୍‍ କରନ୍ତୁ"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"ସ୍କ୍ରିନ୍‍ ରେକର୍ଡିଂ ଡିଲିଟ୍‍ କରିଦିଆଯାଇଛି"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"ସ୍କ୍ରିନ୍‍ ରେକର୍ଡିଂ ଡିଲିଟ୍‍ କରିବାରେ ତ୍ରୁଟି"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"ଅନୁମତି ପାଇବାରେ ଅସଫଳ ହେଲା।"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ସ୍କ୍ରିନ୍ ରେକର୍ଡିଂ ଆରମ୍ଭ କରିବାରେ ତ୍ରୁଟି"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index ed5f40c..98583c0 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"ਮੁੜ-ਚਾਲੂ ਕਰੋ"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"ਰੱਦ ਕਰੋ"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"ਸਾਂਝਾ ਕਰੋ"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"ਮਿਟਾਓ"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"ਸਕ੍ਰੀਨ ਦੀ ਰਿਕਾਰਡਿੰਗ ਰੱਦ ਕੀਤੀ ਗਈ"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਰੱਖਿਅਤ ਕੀਤੀ ਗਈ, ਦੇਖਣ ਲਈ ਟੈਪ ਕਰੋ"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਨੂੰ ਮਿਟਾਇਆ ਗਿਆ"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਨੂੰ ਮਿਟਾਉਣ ਦੌਰਾਨ ਗੜਬੜ ਹੋਈ"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"ਇਜਾਜ਼ਤਾਂ ਪ੍ਰਾਪਤ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਿੰਗ ਨੂੰ ਸ਼ੁਰੂ ਕਰਨ ਵੇਲੇ ਗੜਬੜ ਹੋਈ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index ba30ced..4d5a2ae 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Wznów"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Anuluj"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Udostępnij"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Usuń"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Anulowano nagrywanie zawartości ekranu"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Zapisano nagranie zawartości ekranu – kliknij, by je obejrzeć"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Usunięto nagranie zawartości ekranu"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Błąd podczas usuwania nagrania zawartości ekranu"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Nie udało się uzyskać uprawnień"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Błąd podczas rozpoczynania rejestracji zawartości ekranu"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 715b0e4..ef5c9ce 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Retomar"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Cancelar"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Compartilhar"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Excluir"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Gravação de tela cancelada"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Gravação de tela salva, toque para ver"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Gravação de tela excluída"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Erro ao excluir a gravação de tela"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Não foi possível acessar as permissões"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Erro ao iniciar a gravação de tela"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 1a59de4..541f12c 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Retomar"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Cancelar"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Partilhar"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Eliminar"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Gravação de ecrã cancelada."</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Gravação de ecrã guardada. Toque para ver."</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Gravação de ecrã eliminada."</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Erro ao eliminar a gravação de ecrã."</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Falha ao obter as autorizações."</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Ocorreu um erro ao iniciar a gravação do ecrã."</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 715b0e4..ef5c9ce 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Retomar"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Cancelar"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Compartilhar"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Excluir"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Gravação de tela cancelada"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Gravação de tela salva, toque para ver"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Gravação de tela excluída"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Erro ao excluir a gravação de tela"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Não foi possível acessar as permissões"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Erro ao iniciar a gravação de tela"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index cf428a3..3e2373d 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Reluați"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Anulați"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Trimiteți"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Ștergeți"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Înregistrarea ecranului a fost anulată"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Înregistrarea ecranului a fost salvată. Atingeți pentru vizualizare"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Înregistrarea ecranului a fost ștearsă."</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Eroare la ștergerea înregistrării ecranului"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Nu s-au obținut permisiunile"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Eroare la începerea înregistrării ecranului"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index ba86e95..7747724 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Возобновить"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Отмена"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Поделиться"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Удалить"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Запись видео с экрана отменена"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Запись видео с экрана сохранена. Чтобы открыть ее, нажмите на уведомление."</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Запись видео с экрана удалена"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Не удалось удалить запись видео с экрана"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Не удалось получить необходимые разрешения"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Не удалось начать запись видео с экрана."</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 1cd1ceb..b8813f2 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"නැවත අරඹන්න"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"අවලංගු කරන්න"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"බෙදා ගන්න"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"මකන්න"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"තිර පටිගත කිරීම අවලංගු කරන ලදී"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"තිර පටිගත කිරීම සුරකින ලදී, බැලීමට තට්ටු කරන්න"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"තිර පටිගත කිරීම මකන ලදී"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"තිර පටිගත කිරීම මැකීමේ දෝෂයකි"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"අවසර ලබා ගැනීමට අසමත් විය"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"තිර පටිගත කිරීම ආරම්භ කිරීමේ දෝෂයකි"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 7548607..0f9cb5f 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Obnoviť"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Zrušiť"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Zdieľať"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Odstrániť"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Záznam obrazovky bol zrušený"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Záznam obrazovky bol uložený, zobrazíte ho klepnutím"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Záznam obrazovky bol odstránený"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Pri odstraňovaní záznamu obrazovky sa vyskytla chyba"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Nepodarilo sa získať povolenia"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Pri spustení nahrávania obrazovky sa vyskytla chyba"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 07ad8c1..18db332 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Nadaljuj"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Prekliči"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Deli"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Izbriši"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Snemanje zaslona je preklicano"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Videoposnetek zaslona je shranjen, dotaknite se za ogled"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Videoposnetek zaslona je izbrisan"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Napaka pri brisanju videoposnetka zaslona"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Dovoljenj ni bilo mogoče pridobiti"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Napaka pri začenjanju snemanja zaslona"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index b92744d..57ab1c3 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -108,15 +108,13 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Vazhdo"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Anulo"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Ndaj"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Fshi"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Regjistrimi i ekranit u anulua"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Regjistrimi i ekranit u ruajt, trokit për ta parë"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Regjistrimi i ekranit u fshi"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Gabim gjatë fshirjes së regjistrimit të ekranit"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Marrja e lejeve dështoi"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Gabim gjatë nisjes së regjistrimit të ekranit"</string>
     <string name="usb_preference_title" msgid="1439924437558480718">"Opsionet e transferimit të dosjeve të USB-së"</string>
-    <string name="use_mtp_button_title" msgid="5036082897886518086">"Lidh si një lexues \"media\" (MTP)"</string>
+    <string name="use_mtp_button_title" msgid="5036082897886518086">"Lidh si një luajtës të medias (MTP)"</string>
     <string name="use_ptp_button_title" msgid="7676427598943446826">"Montoje si kamerë (PTP)"</string>
     <string name="installer_cd_button_title" msgid="5499998592841984743">"Instalo \"Transferimi i skedarëve të Android\" për Mac"</string>
     <string name="accessibility_back" msgid="6530104400086152611">"Prapa"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index f57d92b..ec763f6 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Настави"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Откажи"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Дели"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Избриши"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Снимање екрана је отказано"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Снимак екрана је сачуван, додирните да бисте прегледали"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Снимак екрана је избрисан"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Дошло је до проблема при брисању снимка екрана"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Преузимање дозвола није успело"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Грешка при покретању снимања екрана"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 876e337..a0a563d 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Återuppta"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Avbryt"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Dela"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Radera"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Skärminspelningen har avbrutits"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Skärminspelningen har sparats, tryck här om du vill titta på den"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Skärminspelningen har raderats"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Det gick inte att radera skärminspelningen"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Behörighet saknas"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Det gick inte att starta skärminspelningen"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 25e3493..7e264a4 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Endelea"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Ghairi"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Shiriki"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Futa"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Imeghairi mchakato wa kurekodi skrini"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Imehifadhi rekodi ya skrini, gusa ili uangalie"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Imefuta rekodi ya skrini"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Hitilafu imetokea wakati wa kufuta rekodi ya skrini"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Imeshindwa kupata ruhusa"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Hitilafu imetokea wakati wa kuanza kurekodi skrini"</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 19e67db..e0f20ce 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"மீண்டும் தொடங்கு"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"ரத்துசெய்"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"பகிர்"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"நீக்கு"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"திரை ரெக்கார்டிங் ரத்துசெய்யப்பட்டது"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"திரை ரெக்கார்டிங் சேமிக்கப்பட்டது, பார்க்கத் தட்டவும்"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"திரை ரெக்கார்டிங் நீக்கப்பட்டது"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"திரை ரெக்கார்டிங்கை நீக்குவதில் பிழை"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"அனுமதிகளைப் பெற இயலவில்லை"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"ஸ்கிரீன் ரெக்கார்டிங்கைத் தொடங்குவதில் பிழை"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 34ff9b1..3e94b72 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"కొనసాగించు"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"రద్దు చేయి"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"షేర్ చేయి"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"తొలగించు"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"స్క్రీన్ రికార్డ్ రద్దు చేయబడింది"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"స్క్రీన్ రికార్డింగ్ సేవ్ చేయబడింది, చూడటం కోసం నొక్కండి"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"స్క్రీన్ రికార్డింగ్ తొలగించబడింది"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"స్క్రీన్ రికార్డింగ్‌ని తొలగిస్తున్నప్పుడు ఎర్రర్ ఏర్పడింది"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"అనుమతులను పొందడం విఫలమైంది"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"స్క్రీన్ రికార్డింగ్ ప్రారంభించడంలో ఎర్రర్ ఏర్పడింది"</string>
diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml
index 7b1479a..b2d057b 100644
--- a/packages/SystemUI/res/values-television/config.xml
+++ b/packages/SystemUI/res/values-television/config.xml
@@ -28,7 +28,6 @@
     <string-array name="config_systemUIServiceComponents" translatable="false">
         <item>com.android.systemui.util.NotificationChannels</item>
         <item>com.android.systemui.volume.VolumeUI</item>
-        <item>com.android.systemui.stackdivider.Divider</item>
         <item>com.android.systemui.statusbar.tv.TvStatusBar</item>
         <item>com.android.systemui.usb.StorageNotification</item>
         <item>com.android.systemui.power.PowerUI</item>
@@ -42,6 +41,7 @@
         <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
         <item>com.android.systemui.toast.ToastUI</item>
         <item>com.android.systemui.onehanded.OneHandedUI</item>
+        <item>com.android.systemui.wmshell.WMShell</item>
     </string-array>
 
     <!-- Show a separate icon for low and high volume on the volume dialog -->
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index d37be57..088b624 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"ทำต่อ"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"ยกเลิก"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"แชร์"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"ลบ"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"ยกเลิกการบันทึกหน้าจอแล้ว"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"บันทึกการบันทึกหน้าจอแล้ว แตะเพื่อดู"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"ลบการบันทึกหน้าจอแล้ว"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"เกิดข้อผิดพลาดในการลบการบันทึกหน้าจอ"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"ขอสิทธิ์ไม่สำเร็จ"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"เกิดข้อผิดพลาดขณะเริ่มบันทึกหน้าจอ"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index af474c7..e787b17 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Ituloy"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Kanselahin"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Ibahagi"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"I-delete"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Kinansela ang pag-record ng screen"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Na-save ang pag-record ng screen, i-tap para tingnan"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Na-delete ang pag-record ng screen"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Error sa pag-delete sa pag-record ng screen"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Hindi nakuha ang mga pahintulot"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Nagkaroon ng error sa pagsisimula ng pag-record ng screen"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index ee494a2..e339445 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Devam ettir"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"İptal"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Paylaş"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Sil"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Ekran kaydı iptal edildi"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Ekran kaydı tamamlandı, görüntülemek için dokunun"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Ekran kaydı silindi"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Ekran kaydı silinirken hata oluştu"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"İzinler alınamadı"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Ekran kaydı başlatılırken hata oluştu"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index c444c47..f5419f1 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Відновити"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Скасувати"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Поділитися"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Видалити"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Запис екрана скасовано"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Запис екрана збережено. Натисніть, щоб переглянути"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Запис екрана видалено"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Не вдалося видалити запис екрана"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Не вдалось отримати дозволи"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Не вдалося почати запис екрана"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 729ed5a..2abf350 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"دوبارہ شروع کریں"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"منسوخ کریں"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"اشتراک کریں"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"حذف کریں"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"اسکرین ریکارڈنگ منسوخ ہو گئی"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"اسکرین ریکارڈنگ محفوظ ہو گئی، دیکھنے کیلئے تھپتھپائیں"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"اسکرین ریکارڈنگ حذف ہو گئی"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"اسکرین ریکارڈنگ کو حذف کرنے میں خرابی"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"اجازتیں حاصل کرنے میں ناکامی"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"اسکرین ریکارڈنگ شروع کرنے میں خرابی"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index cf1da75..61b10a4 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Davom etish"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Bekor qilish"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Ulashish"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"O‘chirish"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Ekrandan yozib olish bekor qilindi"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Ekrandan yozib olingan video saqlandi. Uni ochish uchun bildirishnomani bosing."</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Ekrandan yozib olingan video o‘chirib tashlandi"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Ekrandan yozib olingan vi olib tashlanmadi"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Zarur ruxsatlar olinmadi"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Ekranni yozib olish boshlanmadi"</string>
@@ -362,7 +360,7 @@
     <string name="accessibility_quick_settings_rotation_value" msgid="2916484894750819251">"<xliff:g id="ID_1">%s</xliff:g> rejimi"</string>
     <string name="quick_settings_rotation_locked_label" msgid="4420863550666310319">"Aylanmaydigan qilingan"</string>
     <string name="quick_settings_rotation_locked_portrait_label" msgid="1194988975270484482">"Tik holat"</string>
-    <string name="quick_settings_rotation_locked_landscape_label" msgid="2000295772687238645">"Eniga"</string>
+    <string name="quick_settings_rotation_locked_landscape_label" msgid="2000295772687238645">"Yotiq"</string>
     <string name="quick_settings_ime_label" msgid="3351174938144332051">"Kiritish usuli"</string>
     <string name="quick_settings_location_label" msgid="2621868789013389163">"Joylashuv"</string>
     <string name="quick_settings_location_off_label" msgid="7923929131443915919">"Joylashuvni aniqlash xizmati yoqilmagan"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 5794bdf..6107270 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Tiếp tục"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Hủy"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Chia sẻ"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Xóa"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Đã hủy bản ghi màn hình"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Đã lưu bản ghi màn hình, nhấn để xem"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Đã xóa bản ghi màn hình"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Lỗi khi xóa bản ghi màn hình"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Không được cấp đủ quyền"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Lỗi khi bắt đầu ghi màn hình"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 41c132c..ffd1995 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"继续"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"取消"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"分享"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"删除"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"已取消录制屏幕"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"屏幕录制内容已保存,点按即可查看"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"已删除屏幕录制内容"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"删除屏幕录制内容时出错"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"无法获取权限"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"启动屏幕录制时出错"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index b2e1b90..1575427 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"繼續"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"取消"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"分享"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"刪除"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"已取消錄影畫面"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"已儲存錄影畫面,輕按即可查看"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"已刪除錄影畫面"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"刪除錄影畫面時發生錯誤"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"無法獲得權限"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"開始錄影畫面時發生錯誤"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 346b1239..bbd8355 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"繼續"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"取消"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"分享"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"刪除"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"已取消錄製螢幕畫面"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"已儲存螢幕畫面錄製內容,輕觸即可查看"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"已刪除螢幕畫面錄製內容"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"刪除螢幕畫面錄製內容時發生錯誤"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"無法取得權限"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"開始錄製螢幕畫面時發生錯誤"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 5abce7f..63b4c61 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -108,10 +108,8 @@
     <string name="screenrecord_resume_label" msgid="4972223043729555575">"Qalisa kabusha"</string>
     <string name="screenrecord_cancel_label" msgid="7850926573274483294">"Khansela"</string>
     <string name="screenrecord_share_label" msgid="5025590804030086930">"Yabelana"</string>
-    <string name="screenrecord_delete_label" msgid="1376347010553987058">"Susa"</string>
     <string name="screenrecord_cancel_success" msgid="1775448688137393901">"Ukurekhoda isikrini kukhanseliwe"</string>
     <string name="screenrecord_save_message" msgid="490522052388998226">"Ukurekhoda isikrini kulondoloziwe, thepha ukuze ubuke"</string>
-    <string name="screenrecord_delete_description" msgid="1604522770162810570">"Ukurekhoda isikrini kususiwe"</string>
     <string name="screenrecord_delete_error" msgid="2870506119743013588">"Iphutha lokususa ukurekhoda isikrini"</string>
     <string name="screenrecord_permission_error" msgid="7856841237023137686">"Yehlulekile ukuthola izimvume"</string>
     <string name="screenrecord_start_error" msgid="2200660692479682368">"Iphutha lokuqala ukurekhoda isikrini"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index ce1ca5a..130bb4f 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -301,7 +301,6 @@
         <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
         <item>com.android.systemui.recents.Recents</item>
         <item>com.android.systemui.volume.VolumeUI</item>
-        <item>com.android.systemui.stackdivider.Divider</item>
         <item>com.android.systemui.statusbar.phone.StatusBar</item>
         <item>com.android.systemui.usb.StorageNotification</item>
         <item>com.android.systemui.power.PowerUI</item>
@@ -323,6 +322,7 @@
         <item>com.android.systemui.accessibility.SystemActions</item>
         <item>com.android.systemui.toast.ToastUI</item>
         <item>com.android.systemui.onehanded.OneHandedUI</item>
+        <item>com.android.systemui.wmshell.WMShell</item>
     </string-array>
 
     <!-- QS tile shape store width. negative implies fill configuration instead of stroke-->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 823c1ff..bfa7532 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -278,14 +278,10 @@
     <string name="screenrecord_cancel_label">Cancel</string>
     <!-- Label for notification action to share screen recording [CHAR LIMIT=35] -->
     <string name="screenrecord_share_label">Share</string>
-    <!-- Label for notification action to delete a screen recording file [CHAR LIMIT=35] -->
-    <string name="screenrecord_delete_label">Delete</string>
     <!-- A toast message shown after successfully canceling a screen recording [CHAR LIMIT=NONE] -->
     <string name="screenrecord_cancel_success">Screen recording canceled</string>
     <!-- Notification text shown after saving a screen recording to prompt the user to view it [CHAR LIMIT=100] -->
     <string name="screenrecord_save_message">Screen recording saved, tap to view</string>
-    <!-- A toast message shown after successfully deleting a screen recording [CHAR LIMIT=NONE] -->
-    <string name="screenrecord_delete_description">Screen recording deleted</string>
     <!-- A toast message shown when there is an error deleting a screen recording [CHAR LIMIT=NONE] -->
     <string name="screenrecord_delete_error">Error deleting screen recording</string>
     <!-- A toast message shown when the screen recording cannot be started due to insufficient permissions [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index ecf1c2c9..5ad8cad 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -22,6 +22,7 @@
 import android.widget.TextClock;
 
 import com.android.internal.colorextraction.ColorExtractor;
+import com.android.keyguard.dagger.KeyguardStatusViewScope;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ClockPlugin;
@@ -36,6 +37,7 @@
 /**
  * Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
  */
+@KeyguardStatusViewScope
 public class KeyguardClockSwitch extends RelativeLayout {
 
     private static final String TAG = "KeyguardClockSwitch";
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index f17f1ca..fe5fcc6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -34,10 +34,11 @@
 public class KeyguardClockSwitchController {
     private static final boolean CUSTOM_CLOCKS_ENABLED = true;
 
+    private final KeyguardClockSwitch mView;
     private final StatusBarStateController mStatusBarStateController;
     private final SysuiColorExtractor mColorExtractor;
     private final ClockManager mClockManager;
-    private KeyguardClockSwitch mView;
+    private final KeyguardSliceViewController mKeyguardSliceViewController;
 
     private final StatusBarStateController.StateListener mStateListener =
             new StatusBarStateController.StateListener() {
@@ -52,9 +53,13 @@
      *
      * The color palette changes when the wallpaper is changed.
      */
-    private final ColorExtractor.OnColorsChangedListener mColorsListener = (extractor, which) -> {
-        if ((which & WallpaperManager.FLAG_LOCK) != 0) {
-            mView.updateColors(getGradientColors());
+    private final ColorExtractor.OnColorsChangedListener mColorsListener =
+            new ColorExtractor.OnColorsChangedListener() {
+        @Override
+        public void onColorsChanged(ColorExtractor extractor, int which) {
+            if ((which & WallpaperManager.FLAG_LOCK) != 0) {
+                mView.updateColors(getGradientColors());
+            }
         }
     };
 
@@ -84,22 +89,27 @@
     };
 
     @Inject
-    public KeyguardClockSwitchController(StatusBarStateController statusBarStateController,
-            SysuiColorExtractor colorExtractor, ClockManager clockManager) {
+    public KeyguardClockSwitchController(KeyguardClockSwitch keyguardClockSwitch,
+            StatusBarStateController statusBarStateController,
+            SysuiColorExtractor colorExtractor, ClockManager clockManager,
+            KeyguardSliceViewController keyguardSliceViewController) {
+        mView = keyguardClockSwitch;
         mStatusBarStateController = statusBarStateController;
         mColorExtractor = colorExtractor;
         mClockManager = clockManager;
+        mKeyguardSliceViewController = keyguardSliceViewController;
     }
 
     /**
      * Attach the controller to the view it relates to.
      */
-    public void attach(KeyguardClockSwitch view) {
-        mView = view;
+    public void init() {
         if (mView.isAttachedToWindow()) {
             mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
         }
         mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
+
+        mKeyguardSliceViewController.init();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
index 6f19613..be21d20 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java
@@ -34,12 +34,15 @@
 import android.view.WindowManager;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.dagger.KeyguardStatusViewComponent;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.navigationbar.NavigationBarController;
 import com.android.systemui.navigationbar.NavigationBarView;
 import com.android.systemui.util.InjectionInflationController;
 
+import javax.inject.Inject;
+
 public class KeyguardDisplayManager {
     protected static final String TAG = "KeyguardDisplayManager";
     private static boolean DEBUG = KeyguardConstants.DEBUG;
@@ -47,6 +50,7 @@
     private final MediaRouter mMediaRouter;
     private final DisplayManager mDisplayService;
     private final InjectionInflationController mInjectableInflater;
+    private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
     private final Context mContext;
 
     private boolean mShowing;
@@ -86,10 +90,13 @@
         }
     };
 
+    @Inject
     public KeyguardDisplayManager(Context context,
-            InjectionInflationController injectableInflater) {
+            InjectionInflationController injectableInflater,
+            KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) {
         mContext = context;
         mInjectableInflater = injectableInflater;
+        mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
         mMediaRouter = mContext.getSystemService(MediaRouter.class);
         mDisplayService = mContext.getSystemService(DisplayManager.class);
         mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
@@ -124,6 +131,7 @@
         Presentation presentation = mPresentations.get(displayId);
         if (presentation == null) {
             final Presentation newPresentation = new KeyguardPresentation(mContext, display,
+                    mKeyguardStatusViewComponentFactory,
                     mInjectableInflater.injectable(LayoutInflater.from(mContext)));
             newPresentation.setOnDismissListener(dialog -> {
                 if (newPresentation.equals(mPresentations.get(displayId))) {
@@ -241,7 +249,9 @@
     static final class KeyguardPresentation extends Presentation {
         private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
         private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
+        private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
         private final LayoutInflater mInjectableLayoutInflater;
+        private KeyguardClockSwitchController mKeyguardClockSwitchController;
         private View mClock;
         private int mUsableWidth;
         private int mUsableHeight;
@@ -259,8 +269,10 @@
         };
 
         KeyguardPresentation(Context context, Display display,
+                KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
                 LayoutInflater injectionLayoutInflater) {
             super(context, display, R.style.Theme_SystemUI_KeyguardPresentation);
+            mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
             mInjectableLayoutInflater = injectionLayoutInflater;
             getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
             setCancelable(false);
@@ -302,6 +314,12 @@
 
             // Avoid screen burn in
             mClock.post(mMoveTextRunnable);
+
+            mKeyguardClockSwitchController = mKeyguardStatusViewComponentFactory
+                    .build(findViewById(R.id.clock))
+                    .getKeyguardClockSwitchController();
+
+            mKeyguardClockSwitchController.init();
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index f639c88..a479bca 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -16,12 +16,6 @@
 
 package com.android.keyguard;
 
-import static android.app.slice.Slice.HINT_LIST_ITEM;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.INVALID_DISPLAY;
-
-import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
-
 import android.animation.LayoutTransition;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
@@ -35,28 +29,19 @@
 import android.graphics.text.LineBreaker;
 import android.net.Uri;
 import android.os.Trace;
-import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.TypedValue;
-import android.view.Display;
 import android.view.View;
 import android.view.animation.Animation;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.Observer;
-import androidx.slice.Slice;
 import androidx.slice.SliceItem;
-import androidx.slice.SliceViewManager;
 import androidx.slice.core.SliceQuery;
-import androidx.slice.widget.ListContent;
 import androidx.slice.widget.RowContent;
 import androidx.slice.widget.SliceContent;
-import androidx.slice.widget.SliceLiveData;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.graphics.ColorUtils;
@@ -64,70 +49,49 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.keyguard.KeyguardSliceProvider;
-import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.wakelock.KeepAwakeAnimationListener;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
-
-import javax.inject.Inject;
-import javax.inject.Named;
+import java.util.Map;
 
 /**
  * View visible under the clock on the lock screen and AoD.
  */
-public class KeyguardSliceView extends LinearLayout implements View.OnClickListener,
-        Observer<Slice>, TunerService.Tunable, ConfigurationController.ConfigurationListener {
+public class KeyguardSliceView extends LinearLayout {
 
     private static final String TAG = "KeyguardSliceView";
     public static final int DEFAULT_ANIM_DURATION = 550;
 
-    private final HashMap<View, PendingIntent> mClickActions;
-    private final ActivityStarter mActivityStarter;
-    private final ConfigurationController mConfigurationController;
     private final LayoutTransition mLayoutTransition;
-    private final TunerService mTunerService;
-    private Uri mKeyguardSliceUri;
     @VisibleForTesting
     TextView mTitle;
     private Row mRow;
     private int mTextColor;
     private float mDarkAmount = 0;
 
-    private LiveData<Slice> mLiveData;
-    private int mDisplayId = INVALID_DISPLAY;
     private int mIconSize;
     private int mIconSizeWithHeader;
     /**
      * Runnable called whenever the view contents change.
      */
     private Runnable mContentChangeListener;
-    private Slice mSlice;
     private boolean mHasHeader;
     private final int mRowWithHeaderPadding;
     private final int mRowPadding;
     private float mRowTextSize;
     private float mRowWithHeaderTextSize;
+    private View.OnClickListener mOnClickListener;
 
-    @Inject
-    public KeyguardSliceView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
-            ActivityStarter activityStarter, ConfigurationController configurationController,
-            TunerService tunerService, @Main Resources resources) {
+    public KeyguardSliceView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
-        mTunerService = tunerService;
-        mClickActions = new HashMap<>();
+        Resources resources = context.getResources();
         mRowPadding = resources.getDimensionPixelSize(R.dimen.subtitle_clock_padding);
         mRowWithHeaderPadding = resources.getDimensionPixelSize(R.dimen.header_subtitle_padding);
-        mActivityStarter = activityStarter;
-        mConfigurationController = configurationController;
 
         mLayoutTransition = new LayoutTransition();
         mLayoutTransition.setStagger(LayoutTransition.CHANGE_APPEARING, DEFAULT_ANIM_DURATION / 2);
@@ -153,39 +117,10 @@
                 R.dimen.widget_label_font_size);
         mRowWithHeaderTextSize = mContext.getResources().getDimensionPixelSize(
                 R.dimen.header_row_font_size);
-        mTitle.setOnClickListener(this);
         mTitle.setBreakStrategy(LineBreaker.BREAK_STRATEGY_BALANCED);
     }
 
     @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-
-        Display display = getDisplay();
-        if (display != null) {
-            mDisplayId = display.getDisplayId();
-        }
-        mTunerService.addTunable(this, Settings.Secure.KEYGUARD_SLICE_URI);
-        // Make sure we always have the most current slice
-        if (mDisplayId == DEFAULT_DISPLAY) {
-            mLiveData.observeForever(this);
-        }
-        mConfigurationController.addCallback(this);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-
-        // TODO(b/117344873) Remove below work around after this issue be fixed.
-        if (mDisplayId == DEFAULT_DISPLAY) {
-            mLiveData.removeObserver(this);
-        }
-        mTunerService.removeTunable(this);
-        mConfigurationController.removeCallback(this);
-    }
-
-    @Override
     public void onVisibilityAggregated(boolean isVisible) {
         super.onVisibilityAggregated(isVisible);
         setLayoutTransition(isVisible ? mLayoutTransition : null);
@@ -198,44 +133,31 @@
         return mHasHeader;
     }
 
-    private void showSlice() {
-        Trace.beginSection("KeyguardSliceView#showSlice");
-        if (mSlice == null) {
-            mTitle.setVisibility(GONE);
-            mRow.setVisibility(GONE);
-            mHasHeader = false;
-            if (mContentChangeListener != null) {
-                mContentChangeListener.run();
-            }
-            Trace.endSection();
-            return;
+    void hideSlice() {
+        mTitle.setVisibility(GONE);
+        mRow.setVisibility(GONE);
+        mHasHeader = false;
+        if (mContentChangeListener != null) {
+            mContentChangeListener.run();
         }
-        mClickActions.clear();
+    }
 
-        ListContent lc = new ListContent(getContext(), mSlice);
-        SliceContent headerContent = lc.getHeader();
-        mHasHeader = headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM);
-        List<SliceContent> subItems = new ArrayList<>();
-        for (int i = 0; i < lc.getRowItems().size(); i++) {
-            SliceContent subItem = lc.getRowItems().get(i);
-            String itemUri = subItem.getSliceItem().getSlice().getUri().toString();
-            // Filter out the action row
-            if (!KeyguardSliceProvider.KEYGUARD_ACTION_URI.equals(itemUri)) {
-                subItems.add(subItem);
-            }
-        }
+    Map<View, PendingIntent> showSlice(RowContent header, List<SliceContent> subItems) {
+        Trace.beginSection("KeyguardSliceView#showSlice");
+        mHasHeader = header != null;
+        Map<View, PendingIntent> clickActions = new HashMap<>();
+
         if (!mHasHeader) {
             mTitle.setVisibility(GONE);
         } else {
             mTitle.setVisibility(VISIBLE);
 
-            RowContent header = lc.getHeader();
             SliceItem mainTitle = header.getTitleItem();
             CharSequence title = mainTitle != null ? mainTitle.getText() : null;
             mTitle.setText(title);
             if (header.getPrimaryAction() != null
                     && header.getPrimaryAction().getAction() != null) {
-                mClickActions.put(mTitle, header.getPrimaryAction().getAction());
+                clickActions.put(mTitle, header.getPrimaryAction().getAction());
             }
         }
 
@@ -265,7 +187,7 @@
             if (rc.getPrimaryAction() != null) {
                 pendingIntent = rc.getPrimaryAction().getAction();
             }
-            mClickActions.put(button, pendingIntent);
+            clickActions.put(button, pendingIntent);
 
             final SliceItem titleItem = rc.getTitleItem();
             button.setText(titleItem == null ? null : titleItem.getText());
@@ -286,14 +208,14 @@
                 }
             }
             button.setCompoundDrawables(iconDrawable, null, null, null);
-            button.setOnClickListener(this);
+            button.setOnClickListener(mOnClickListener);
             button.setClickable(pendingIntent != null);
         }
 
         // Removing old views
         for (int i = 0; i < mRow.getChildCount(); i++) {
             View child = mRow.getChildAt(i);
-            if (!mClickActions.containsKey(child)) {
+            if (!clickActions.containsKey(child)) {
                 mRow.removeView(child);
                 i--;
             }
@@ -303,6 +225,8 @@
             mContentChangeListener.run();
         }
         Trace.endSection();
+
+        return clickActions;
     }
 
     public void setDarkAmount(float darkAmount) {
@@ -323,14 +247,6 @@
         }
     }
 
-    @Override
-    public void onClick(View v) {
-        final PendingIntent action = mClickActions.get(v);
-        if (action != null) {
-            mActivityStarter.startPendingIntentDismissingKeyguard(action);
-        }
-    }
-
     /**
      * Runnable that gets invoked every time the title or the row visibility changes.
      * @param contentChangeListener The listener.
@@ -339,43 +255,6 @@
         mContentChangeListener = contentChangeListener;
     }
 
-    /**
-     * LiveData observer lifecycle.
-     * @param slice the new slice content.
-     */
-    @Override
-    public void onChanged(Slice slice) {
-        mSlice = slice;
-        showSlice();
-    }
-
-    @Override
-    public void onTuningChanged(String key, String newValue) {
-        setupUri(newValue);
-    }
-
-    /**
-     * Sets the slice provider Uri.
-     */
-    public void setupUri(String uriString) {
-        if (uriString == null) {
-            uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI;
-        }
-
-        boolean wasObserving = false;
-        if (mLiveData != null && mLiveData.hasActiveObservers()) {
-            wasObserving = true;
-            mLiveData.removeObserver(this);
-        }
-
-        mKeyguardSliceUri = Uri.parse(uriString);
-        mLiveData = SliceLiveData.fromUri(mContext, mKeyguardSliceUri);
-
-        if (wasObserving) {
-            mLiveData.observeForever(this);
-        }
-    }
-
     @VisibleForTesting
     int getTextColor() {
         return ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount);
@@ -387,8 +266,7 @@
         updateTextColors();
     }
 
-    @Override
-    public void onDensityOrFontScaleChanged() {
+    void onDensityOrFontScaleChanged() {
         mIconSize = mContext.getResources().getDimensionPixelSize(R.dimen.widget_icon_size);
         mIconSizeWithHeader = (int) mContext.getResources().getDimension(R.dimen.header_icon_size);
         mRowTextSize = mContext.getResources().getDimensionPixelSize(
@@ -397,37 +275,21 @@
                 R.dimen.header_row_font_size);
     }
 
-    public void refresh() {
-        Slice slice;
-        Trace.beginSection("KeyguardSliceView#refresh");
-        // We can optimize performance and avoid binder calls when we know that we're bound
-        // to a Slice on the same process.
-        if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) {
-            KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance();
-            if (instance != null) {
-                slice = instance.onBindSlice(mKeyguardSliceUri);
-            } else {
-                Log.w(TAG, "Keyguard slice not bound yet?");
-                slice = null;
-            }
-        } else {
-            slice = SliceViewManager.getInstance(getContext()).bindSlice(mKeyguardSliceUri);
-        }
-        onChanged(slice);
-        Trace.endSection();
-    }
-
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("KeyguardSliceView:");
-        pw.println("  mClickActions: " + mClickActions);
         pw.println("  mTitle: " + (mTitle == null ? "null" : mTitle.getVisibility() == VISIBLE));
         pw.println("  mRow: " + (mRow == null ? "null" : mRow.getVisibility() == VISIBLE));
         pw.println("  mTextColor: " + Integer.toHexString(mTextColor));
         pw.println("  mDarkAmount: " + mDarkAmount);
-        pw.println("  mSlice: " + mSlice);
         pw.println("  mHasHeader: " + mHasHeader);
     }
 
+    @Override
+    public void setOnClickListener(View.OnClickListener onClickListener) {
+        mOnClickListener = onClickListener;
+        mTitle.setOnClickListener(onClickListener);
+    }
+
     public static class Row extends LinearLayout {
 
         /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
new file mode 100644
index 0000000..2470b95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -0,0 +1,239 @@
+/*
+ * 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.keyguard;
+
+import static android.app.slice.Slice.HINT_LIST_ITEM;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.app.PendingIntent;
+import android.net.Uri;
+import android.os.Trace;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.Display;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.Observer;
+import androidx.slice.Slice;
+import androidx.slice.SliceViewManager;
+import androidx.slice.widget.ListContent;
+import androidx.slice.widget.RowContent;
+import androidx.slice.widget.SliceContent;
+import androidx.slice.widget.SliceLiveData;
+
+import com.android.keyguard.dagger.KeyguardStatusViewScope;
+import com.android.systemui.Dumpable;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardSliceProvider;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.tuner.TunerService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.inject.Inject;
+
+/** Controller for a {@link KeyguardSliceView}. */
+@KeyguardStatusViewScope
+public class KeyguardSliceViewController implements Dumpable {
+    private static final String TAG = "KeyguardSliceViewCtrl";
+
+    private final KeyguardSliceView mView;
+    private final KeyguardStatusView mKeyguardStatusView;
+    private final ActivityStarter mActivityStarter;
+    private final ConfigurationController mConfigurationController;
+    private final TunerService mTunerService;
+    private final DumpManager mDumpManager;
+    private int mDisplayId;
+    private LiveData<Slice> mLiveData;
+    private Uri mKeyguardSliceUri;
+    private Slice mSlice;
+    private Map<View, PendingIntent> mClickActions;
+
+    private final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
+            new View.OnAttachStateChangeListener() {
+
+                @Override
+                public void onViewAttachedToWindow(View v) {
+
+                    Display display = mView.getDisplay();
+                    if (display != null) {
+                        mDisplayId = display.getDisplayId();
+                    }
+                    mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI);
+                    // Make sure we always have the most current slice
+                    if (mDisplayId == DEFAULT_DISPLAY && mLiveData != null) {
+                        mLiveData.observeForever(mObserver);
+                    }
+                    mConfigurationController.addCallback(mConfigurationListener);
+                    mDumpManager.registerDumpable(
+                            TAG + "@" + Integer.toHexString(
+                                    KeyguardSliceViewController.this.hashCode()),
+                            KeyguardSliceViewController.this);
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View v) {
+
+                    // TODO(b/117344873) Remove below work around after this issue be fixed.
+                    if (mDisplayId == DEFAULT_DISPLAY) {
+                        mLiveData.removeObserver(mObserver);
+                    }
+                    mTunerService.removeTunable(mTunable);
+                    mConfigurationController.removeCallback(mConfigurationListener);
+                    mDumpManager.unregisterDumpable(TAG);
+                }
+            };
+
+    TunerService.Tunable mTunable = (key, newValue) -> setupUri(newValue);
+
+    ConfigurationController.ConfigurationListener mConfigurationListener =
+            new ConfigurationController.ConfigurationListener() {
+        @Override
+        public void onDensityOrFontScaleChanged() {
+            mView.onDensityOrFontScaleChanged();
+        }
+    };
+
+    Observer<Slice> mObserver = new Observer<Slice>() {
+        @Override
+        public void onChanged(Slice slice) {
+            mSlice = slice;
+            showSlice(slice);
+        }
+    };
+
+    private View.OnClickListener mOnClickListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            final PendingIntent action = mClickActions.get(v);
+            if (action != null && mActivityStarter != null) {
+                mActivityStarter.startPendingIntentDismissingKeyguard(action);
+            }
+        }
+    };
+
+    @Inject
+    public KeyguardSliceViewController(KeyguardSliceView keyguardSliceView,
+            KeyguardStatusView keyguardStatusView, ActivityStarter activityStarter,
+            ConfigurationController configurationController, TunerService tunerService,
+            DumpManager dumpManager) {
+        mView = keyguardSliceView;
+        mKeyguardStatusView = keyguardStatusView;
+        mActivityStarter = activityStarter;
+        mConfigurationController = configurationController;
+        mTunerService = tunerService;
+        mDumpManager = dumpManager;
+    }
+
+    /** Initialize the controller. */
+    public void init() {
+        if (mView.isAttachedToWindow()) {
+            mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
+        }
+        mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
+        mView.setOnClickListener(mOnClickListener);
+        // TODO: remove the line below.
+        mKeyguardStatusView.setKeyguardSliceViewController(this);
+    }
+
+    /**
+     * Sets the slice provider Uri.
+     */
+    public void setupUri(String uriString) {
+        if (uriString == null) {
+            uriString = KeyguardSliceProvider.KEYGUARD_SLICE_URI;
+        }
+
+        boolean wasObserving = false;
+        if (mLiveData != null && mLiveData.hasActiveObservers()) {
+            wasObserving = true;
+            mLiveData.removeObserver(mObserver);
+        }
+
+        mKeyguardSliceUri = Uri.parse(uriString);
+        mLiveData = SliceLiveData.fromUri(mView.getContext(), mKeyguardSliceUri);
+
+        if (wasObserving) {
+            mLiveData.observeForever(mObserver);
+        }
+    }
+
+    /**
+     * Update contents of the view.
+     */
+    public void refresh() {
+        Slice slice;
+        Trace.beginSection("KeyguardSliceViewController#refresh");
+        // We can optimize performance and avoid binder calls when we know that we're bound
+        // to a Slice on the same process.
+        if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) {
+            KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance();
+            if (instance != null) {
+                slice = instance.onBindSlice(mKeyguardSliceUri);
+            } else {
+                Log.w(TAG, "Keyguard slice not bound yet?");
+                slice = null;
+            }
+        } else {
+            // TODO: Make SliceViewManager injectable
+            slice = SliceViewManager.getInstance(mView.getContext()).bindSlice(mKeyguardSliceUri);
+        }
+        mObserver.onChanged(slice);
+        Trace.endSection();
+    }
+
+    void showSlice(Slice slice) {
+        Trace.beginSection("KeyguardSliceViewController#showSlice");
+        if (slice == null) {
+            mView.hideSlice();
+            Trace.endSection();
+            return;
+        }
+
+        ListContent lc = new ListContent(slice);
+        RowContent headerContent = lc.getHeader();
+        boolean hasHeader =
+                headerContent != null && !headerContent.getSliceItem().hasHint(HINT_LIST_ITEM);
+
+        List<SliceContent> subItems = lc.getRowItems().stream().filter(sliceContent -> {
+            String itemUri = sliceContent.getSliceItem().getSlice().getUri().toString();
+            // Filter out the action row
+            return !KeyguardSliceProvider.KEYGUARD_ACTION_URI.equals(itemUri);
+        }).collect(Collectors.toList());
+
+
+        mClickActions = mView.showSlice(hasHeader ? headerContent : null, subItems);
+
+        Trace.endSection();
+    }
+
+
+    @Override
+    public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+        pw.println("  mSlice: " + mSlice);
+        pw.println("  mClickActions: " + mClickActions);
+
+        mKeyguardStatusView.dump(fd, pw, args);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 4c6aafb..6e11174 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -32,7 +32,6 @@
 import android.util.TypedValue;
 import android.view.View;
 import android.widget.GridLayout;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import androidx.core.graphics.ColorUtils;
@@ -56,7 +55,6 @@
     private final LockPatternUtils mLockPatternUtils;
     private final IActivityManager mIActivityManager;
 
-    private LinearLayout mStatusViewContainer;
     private TextView mLogoutView;
     private KeyguardClockSwitch mClockView;
     private TextView mOwnerInfo;
@@ -64,6 +62,7 @@
     private View mNotificationIcons;
     private Runnable mPendingMarqueeStart;
     private Handler mHandler;
+    private KeyguardSliceViewController mKeyguardSliceViewController;
 
     private boolean mPulsing;
     private float mDarkAmount = 0;
@@ -179,7 +178,6 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mStatusViewContainer = findViewById(R.id.status_view_container);
         mLogoutView = findViewById(R.id.logout);
         mNotificationIcons = findViewById(R.id.clock_notification_icon_container);
         if (mLogoutView != null) {
@@ -250,7 +248,7 @@
 
     public void dozeTimeTick() {
         refreshTime();
-        mKeyguardSlice.refresh();
+        mKeyguardSliceViewController.refresh();
     }
 
     private void refreshTime() {
@@ -456,4 +454,9 @@
             Log.e(TAG, "Failed to logout user", re);
         }
     }
+
+    // TODO: remove this method when a controller is available.
+    void setKeyguardSliceViewController(KeyguardSliceViewController keyguardSliceViewController) {
+        mKeyguardSliceViewController = keyguardSliceViewController;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java
new file mode 100644
index 0000000..21ccff7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java
@@ -0,0 +1,39 @@
+/*
+ * 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.keyguard.dagger;
+
+import com.android.keyguard.KeyguardClockSwitchController;
+import com.android.keyguard.KeyguardStatusView;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Subcomponent for helping work with KeyguardStatusView and its children.
+ */
+@Subcomponent(modules = {KeyguardStatusViewModule.class})
+@KeyguardStatusViewScope
+public interface KeyguardStatusViewComponent {
+    /** Simple factory for {@link KeyguardStatusViewComponent}. */
+    @Subcomponent.Factory
+    interface Factory {
+        KeyguardStatusViewComponent build(@BindsInstance KeyguardStatusView presentation);
+    }
+
+    /** Builds a {@link com.android.keyguard.KeyguardClockSwitchController}. */
+    KeyguardClockSwitchController getKeyguardClockSwitchController();
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewModule.java
new file mode 100644
index 0000000..1d51e59
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewModule.java
@@ -0,0 +1,39 @@
+/*
+ * 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.keyguard.dagger;
+
+import com.android.keyguard.KeyguardClockSwitch;
+import com.android.keyguard.KeyguardSliceView;
+import com.android.keyguard.KeyguardStatusView;
+import com.android.systemui.R;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** Dagger module for {@link KeyguardStatusViewComponent}. */
+@Module
+public abstract class KeyguardStatusViewModule {
+    @Provides
+    static KeyguardClockSwitch getKeyguardClockSwitch(KeyguardStatusView keyguardPresentation) {
+        return keyguardPresentation.findViewById(R.id.keyguard_clock_container);
+    }
+
+    @Provides
+    static KeyguardSliceView getKeyguardSliceView(KeyguardClockSwitch keyguardClockSwitch) {
+        return keyguardClockSwitch.findViewById(R.id.keyguard_status_area);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewScope.java
similarity index 79%
rename from packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
rename to packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewScope.java
index 114c30e..880822a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewScope.java
@@ -14,17 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone.dagger;
+package com.android.keyguard.dagger;
 
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
 import java.lang.annotation.Documented;
 import java.lang.annotation.Retention;
 
-import javax.inject.Qualifier;
+import javax.inject.Scope;
 
-@Qualifier
+/**
+ * Scope annotation for singleton items within the StatusBarComponent.
+ */
 @Documented
 @Retention(RUNTIME)
-public @interface PipMenuActivityClass {
-}
+@Scope
+public @interface KeyguardStatusViewScope {}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 748a9c9..27809b5 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -65,7 +65,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
 import com.android.systemui.shared.system.PackageManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -324,7 +323,6 @@
     @Inject Lazy<DisplayImeController> mDisplayImeController;
     @Inject Lazy<RecordingController> mRecordingController;
     @Inject Lazy<ProtoTracer> mProtoTracer;
-    @Inject Lazy<Divider> mDivider;
 
     @Inject
     public Dependency() {
@@ -521,7 +519,6 @@
         mProviders.put(AutoHideController.class, mAutoHideController::get);
 
         mProviders.put(RecordingController.class, mRecordingController::get);
-        mProviders.put(Divider.class, mDivider::get);
 
         Dependency.setInstance(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index d1149d3..f4c865e 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -21,7 +21,7 @@
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -38,7 +38,7 @@
  *
  * NOTE: Clients of this class should take care to pass in the correct user context when querying
  * settings, otherwise you will always read/write for user 0 which is almost never what you want.
- * See {@link CurrentUserContextTracker} for a simple way to get the current context
+ * See {@link UserContextProvider} for a simple way to get the current context
  */
 public final class Prefs {
     private Prefs() {} // no instantation
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index d17ca404..e91284b 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -16,13 +16,14 @@
 
 package com.android.systemui;
 
+import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.NonNull;
-import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.RectF;
 import android.os.Handler;
@@ -115,24 +116,24 @@
     private final ArrayMap<View, Animator> mDismissPendingMap = new ArrayMap<>();
 
     public SwipeHelper(
-            int swipeDirection, Callback callback, Context context, FalsingManager falsingManager) {
+            int swipeDirection, Callback callback, Resources resources,
+            ViewConfiguration viewConfiguration, FalsingManager falsingManager) {
         mCallback = callback;
         mHandler = new Handler();
         mSwipeDirection = swipeDirection;
         mVelocityTracker = VelocityTracker.obtain();
-        final ViewConfiguration configuration = ViewConfiguration.get(context);
-        mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
-        mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier();
+        mPagingTouchSlop = viewConfiguration.getScaledPagingTouchSlop();
+        mSlopMultiplier = viewConfiguration.getScaledAmbiguousGestureMultiplier();
 
         // Extra long-press!
         mLongPressTimeout = (long) (ViewConfiguration.getLongPressTimeout() * 1.5f);
 
-        Resources res = context.getResources();
-        mDensityScale =  res.getDisplayMetrics().density;
-        mFalsingThreshold = res.getDimensionPixelSize(R.dimen.swipe_helper_falsing_threshold);
-        mFadeDependingOnAmountSwiped = res.getBoolean(R.bool.config_fadeDependingOnAmountSwiped);
+        mDensityScale =  resources.getDisplayMetrics().density;
+        mFalsingThreshold = resources.getDimensionPixelSize(R.dimen.swipe_helper_falsing_threshold);
+        mFadeDependingOnAmountSwiped = resources.getBoolean(
+                R.bool.config_fadeDependingOnAmountSwiped);
         mFalsingManager = falsingManager;
-        mFlingAnimationUtils = new FlingAnimationUtils(res.getDisplayMetrics(),
+        mFlingAnimationUtils = new FlingAnimationUtils(resources.getDisplayMetrics(),
                 getMaxEscapeAnimDuration() / 1000f);
     }
 
@@ -697,14 +698,15 @@
         float translation = getTranslation(mCurrView);
         return ev.getActionMasked() == MotionEvent.ACTION_UP
                 && !mFalsingManager.isUnlockingDisabled()
-                && !isFalseGesture(ev) && (swipedFastEnough() || swipedFarEnough())
+                && !isFalseGesture() && (swipedFastEnough() || swipedFarEnough())
                 && mCallback.canChildBeDismissedInDirection(mCurrView, translation > 0);
     }
 
-    public boolean isFalseGesture(MotionEvent ev) {
+    /** Returns true if the gesture should be rejected. */
+    public boolean isFalseGesture() {
         boolean falsingDetected = mCallback.isAntiFalsingNeeded();
         if (mFalsingManager.isClassifierEnabled()) {
-            falsingDetected = falsingDetected && mFalsingManager.isFalseTouch();
+            falsingDetected = falsingDetected && mFalsingManager.isFalseTouch(NOTIFICATION_DISMISS);
         } else {
             falsingDetected = falsingDetected && !mTouchAboveFalsingThreshold;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index f5c3649..941fd6f 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -38,6 +38,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 
 /**
@@ -83,11 +84,16 @@
 
     public SystemUIFactory() {}
 
-    private void init(Context context) {
+    private void init(Context context) throws ExecutionException, InterruptedException {
         mRootComponent = buildGlobalRootComponent(context);
+        // Stand up WMComponent
         mWMComponent = mRootComponent.getWMComponentBuilder().build();
-        // TODO: use WMComponent to pass APIs into the SysUIComponent.
-        mSysUIComponent = mRootComponent.getSysUIComponent().build();
+
+        // And finally, retrieve whatever SysUI needs from WMShell and build SysUI.
+        // TODO: StubAPIClass is just a placeholder.
+        mSysUIComponent = mRootComponent.getSysUIComponent()
+                .setStubAPIClass(mWMComponent.createStubAPIClass())
+                .build();
 
         // Every other part of our codebase currently relies on Dependency, so we
         // really need to ensure the Dependency gets initialized early on.
@@ -101,10 +107,15 @@
                 .build();
     }
 
+
     public GlobalRootComponent getRootComponent() {
         return mRootComponent;
     }
 
+    public WMComponent getWMComponent() {
+        return mWMComponent;
+    }
+
     public SysUIComponent getSysUIComponent() {
         return mSysUIComponent;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index eeb3b28..a3339f6 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -19,6 +19,7 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.location.LocationManager;
 import android.media.AudioManager;
 import android.media.AudioRecordingConfiguration;
 import android.os.Handler;
@@ -66,6 +67,13 @@
 
     private final AppOpsManager mAppOps;
     private final AudioManager mAudioManager;
+    private final LocationManager mLocationManager;
+
+    // mLocationProviderPackages are cached and updated only occasionally
+    private static final long LOCATION_PROVIDER_UPDATE_FREQUENCY_MS = 30000;
+    private long mLastLocationProviderPackageUpdate;
+    private List<String> mLocationProviderPackages;
+
     private H mBGHandler;
     private final List<AppOpsController.Callback> mCallbacks = new ArrayList<>();
     private final SparseArray<Set<Callback>> mCallbacksByCode = new SparseArray<>();
@@ -83,8 +91,10 @@
     protected static final int[] OPS = new int[] {
             AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION,
             AppOpsManager.OP_CAMERA,
+            AppOpsManager.OP_PHONE_CALL_CAMERA,
             AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
             AppOpsManager.OP_RECORD_AUDIO,
+            AppOpsManager.OP_PHONE_CALL_MICROPHONE,
             AppOpsManager.OP_COARSE_LOCATION,
             AppOpsManager.OP_FINE_LOCATION
     };
@@ -105,6 +115,7 @@
             mCallbacksByCode.put(OPS[i], new ArraySet<>());
         }
         mAudioManager = audioManager;
+        mLocationManager = context.getSystemService(LocationManager.class);
         dumpManager.registerDumpable(TAG, this);
     }
 
@@ -288,6 +299,26 @@
         return isUserVisible(item.getCode(), item.getUid(), item.getPackageName());
     }
 
+    /**
+     * Checks if a package is the current location provider.
+     *
+     * <p>Data is cached to avoid too many calls into system server
+     *
+     * @param packageName The package that might be the location provider
+     *
+     * @return {@code true} iff the package is the location provider.
+     */
+    private boolean isLocationProvider(String packageName) {
+        long now = System.currentTimeMillis();
+
+        if (mLastLocationProviderPackageUpdate + LOCATION_PROVIDER_UPDATE_FREQUENCY_MS < now) {
+            mLastLocationProviderPackageUpdate = now;
+            mLocationProviderPackages = mLocationManager.getProviderPackages(
+                    LocationManager.FUSED_PROVIDER);
+        }
+
+        return mLocationProviderPackages.contains(packageName);
+    }
 
     /**
      * Does the app-op, uid and package name, refer to an operation that should be shown to the
@@ -303,7 +334,13 @@
         // does not correspond to a platform permission
         // which may be user sensitive, so for now always show it to the user.
         if (appOpCode == AppOpsManager.OP_SYSTEM_ALERT_WINDOW
-                || appOpCode == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION) {
+                || appOpCode == AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION
+                || appOpCode == AppOpsManager.OP_PHONE_CALL_CAMERA
+                || appOpCode == AppOpsManager.OP_PHONE_CALL_MICROPHONE) {
+            return true;
+        }
+
+        if (appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName)) {
             return true;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java b/packages/SystemUI/src/com/android/systemui/appops/dagger/AppOpsModule.java
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
copy to packages/SystemUI/src/com/android/systemui/appops/dagger/AppOpsModule.java
index 114c30e..d4cc3f3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/dagger/AppOpsModule.java
@@ -14,17 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone.dagger;
+package com.android.systemui.appops.dagger;
 
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.appops.AppOpsControllerImpl;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
+import dagger.Binds;
+import dagger.Module;
 
-import javax.inject.Qualifier;
+/** Dagger Module for code in the appops package. */
+@Module
+public interface AppOpsModule {
+    /** */
+    @Binds
+    AppOpsController provideAppOpsController(AppOpsControllerImpl controllerImpl);
 
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface PipMenuActivityClass {
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index d4e6506b..0892612 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -111,7 +111,7 @@
             // VerifyCredentialResponse so that we can request a Gatekeeper HAT with the
             // Gatekeeper Password and operationId.
             mPendingLockCheck = LockPatternChecker.verifyCredential(mLockPatternUtils,
-                    password, mEffectiveUserId, LockPatternUtils.VERIFY_FLAG_RETURN_GK_PW,
+                    password, mEffectiveUserId, LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE,
                     this::onCredentialVerified);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
index ad89ae8..ab8162f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
@@ -75,7 +75,7 @@
                         mLockPatternUtils,
                         credential,
                         mEffectiveUserId,
-                        LockPatternUtils.VERIFY_FLAG_RETURN_GK_PW,
+                        LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE,
                         this::onPatternVerified);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index a8f6f85..b44ff29 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -292,10 +292,12 @@
             // The response passed into this method contains the Gatekeeper Password. We still
             // have to request Gatekeeper to create a Hardware Auth Token with the
             // Gatekeeper Password and Challenge (keystore operationId in this case)
-            final VerifyCredentialResponse gkResponse = mLockPatternUtils.verifyGatekeeperPassword(
-                    response.getGatekeeperPw(), mOperationId, mEffectiveUserId);
+            final long pwHandle = response.getGatekeeperPasswordHandle();
+            final VerifyCredentialResponse gkResponse = mLockPatternUtils
+                    .verifyGatekeeperPasswordHandle(pwHandle, mOperationId, mEffectiveUserId);
 
             mCallback.onCredentialMatched(gkResponse.getGatekeeperHAT());
+            mLockPatternUtils.removeGatekeeperPasswordHandle(pwHandle);
         } else {
             if (timeoutMs > 0) {
                 mHandler.removeCallbacks(mClearErrorRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
index 646e620..6961b45 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java
@@ -70,7 +70,7 @@
     }
 
     @Override
-    public boolean isFalseTouch() {
+    public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
         return mIsFalseTouch;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index cc64fb5..decaec1 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -262,7 +262,7 @@
     /**
      * @return true if the classifier determined that this is not a human interacting with the phone
      */
-    public boolean isFalseTouch() {
+    public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
         if (FalsingLog.ENABLED) {
             // We're getting some false wtfs from touches that happen after the device went
             // to sleep. Only report missing sessions that happen when the device is interactive.
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index 83b6df3..2c31862 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -187,8 +187,8 @@
     }
 
     @Override
-    public boolean isFalseTouch() {
-        return mInternalFalsingManager.isFalseTouch();
+    public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
+        return mInternalFalsingManager.isFalseTouch(interactionType);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index a50f9ce..9d847ca 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -189,7 +189,8 @@
     }
 
     @Override
-    public boolean isFalseTouch() {
+    public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
+        mDataProvider.setInteractionType(interactionType);
         if (!mDataProvider.isDirty()) {
             return mPreviousResult;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
index ea46441..8d06748 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java
@@ -116,7 +116,10 @@
      * interactionType is defined by {@link com.android.systemui.classifier.Classifier}.
      */
     final void setInteractionType(@Classifier.InteractionType int interactionType) {
-        this.mInteractionType = interactionType;
+        if (mInteractionType != interactionType) {
+            mInteractionType = interactionType;
+            mDirty = true;
+        }
     }
 
     public boolean isDirty() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
index 1bda841..d930c98 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
@@ -87,6 +87,10 @@
      * @param list a list of favorite controls. The list will be stored in the same order.
      */
     fun storeFavorites(structures: List<StructureInfo>) {
+        if (structures.isEmpty() && !file.exists()) {
+            // Do not create a new file to store nothing
+            return
+        }
         executor.execute {
             Log.d(TAG, "Saving data to file: $file")
             val atomicFile = AtomicFile(file)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
index c95e81c..596e440 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultServiceBinder.java
@@ -66,12 +66,6 @@
     @ClassKey(SystemUIAuxiliaryDumpService.class)
     public abstract Service bindSystemUIAuxiliaryDumpService(SystemUIAuxiliaryDumpService service);
 
-    /** */
-    @Binds
-    @IntoMap
-    @ClassKey(TakeScreenshotService.class)
-    public abstract Service bindTakeScreenshotService(TakeScreenshotService service);
-
     /** Inject into RecordingService */
     @Binds
     @IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
deleted file mode 100644
index e2a6d6c..0000000
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.dagger;
-
-import com.android.systemui.ActivityStarterDelegate;
-import com.android.systemui.appops.AppOpsController;
-import com.android.systemui.appops.AppOpsControllerImpl;
-import com.android.systemui.classifier.FalsingManagerProxy;
-import com.android.systemui.controls.dagger.ControlsModule;
-import com.android.systemui.globalactions.GlobalActionsComponent;
-import com.android.systemui.globalactions.GlobalActionsImpl;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.plugins.DarkIconDispatcher;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.GlobalActions;
-import com.android.systemui.plugins.VolumeDialogController;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.power.PowerNotificationWarnings;
-import com.android.systemui.power.PowerUI;
-import com.android.systemui.qs.QSHost;
-import com.android.systemui.qs.QSTileHost;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.StatusBarStateControllerImpl;
-import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
-import com.android.systemui.statusbar.phone.ManagedProfileController;
-import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBarIconController;
-import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
-import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
-import com.android.systemui.statusbar.policy.CastController;
-import com.android.systemui.statusbar.policy.CastControllerImpl;
-import com.android.systemui.statusbar.policy.ExtensionController;
-import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
-import com.android.systemui.statusbar.policy.FlashlightController;
-import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
-import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.HotspotControllerImpl;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl;
-import com.android.systemui.statusbar.policy.LocationController;
-import com.android.systemui.statusbar.policy.LocationControllerImpl;
-import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.NetworkControllerImpl;
-import com.android.systemui.statusbar.policy.NextAlarmController;
-import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
-import com.android.systemui.statusbar.policy.RotationLockController;
-import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
-import com.android.systemui.statusbar.policy.SecurityController;
-import com.android.systemui.statusbar.policy.SecurityControllerImpl;
-import com.android.systemui.statusbar.policy.SensorPrivacyController;
-import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
-import com.android.systemui.statusbar.policy.UserInfoController;
-import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
-import com.android.systemui.statusbar.policy.ZenModeController;
-import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
-import com.android.systemui.tuner.TunerService;
-import com.android.systemui.tuner.TunerServiceImpl;
-import com.android.systemui.util.RingerModeTracker;
-import com.android.systemui.util.RingerModeTrackerImpl;
-import com.android.systemui.volume.VolumeComponent;
-import com.android.systemui.volume.VolumeDialogComponent;
-import com.android.systemui.volume.VolumeDialogControllerImpl;
-
-import dagger.Binds;
-import dagger.Module;
-
-/**
- * Maps interfaces to implementations for use with Dagger.
- */
-@Module(includes = {ControlsModule.class})
-public abstract class DependencyBinder {
-
-    /**
-     */
-    @Binds
-    public abstract ActivityStarter provideActivityStarter(ActivityStarterDelegate delegate);
-
-    /**
-     */
-    @Binds
-    public abstract BluetoothController provideBluetoothController(
-            BluetoothControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract GlobalActions provideGlobalActions(GlobalActionsImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract GlobalActions.GlobalActionsManager provideGlobalActionsManager(
-            GlobalActionsComponent controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract LocationController provideLocationController(
-            LocationControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract RotationLockController provideRotationLockController(
-            RotationLockControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract NetworkController provideNetworkController(
-            NetworkControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract ZenModeController provideZenModeController(
-            ZenModeControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract HotspotController provideHotspotController(
-            HotspotControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract AppOpsController provideAppOpsController(
-            AppOpsControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract NotificationRemoteInputManager.Callback provideNotificationRemoteInputManager(
-            StatusBarRemoteInputCallback callbackImpl);
-
-    /**
-     */
-    @Binds
-    public abstract CastController provideCastController(CastControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract FlashlightController provideFlashlightController(
-            FlashlightControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract KeyguardStateController provideKeyguardMonitor(
-            KeyguardStateControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract UserInfoController provideUserInfoContrller(
-            UserInfoControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract ManagedProfileController provideManagedProfileController(
-            ManagedProfileControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract NextAlarmController provideNextAlarmController(
-            NextAlarmControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract SecurityController provideSecurityController(
-            SecurityControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract TunerService provideTunerService(TunerServiceImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract DarkIconDispatcher provideDarkIconDispatcher(
-            DarkIconDispatcherImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract StatusBarStateController provideStatusBarStateController(
-            StatusBarStateControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract SysuiStatusBarStateController providesSysuiStatusBarStateController(
-            StatusBarStateControllerImpl statusBarStateControllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract StatusBarIconController provideStatusBarIconController(
-            StatusBarIconControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract ExtensionController provideExtensionController(
-            ExtensionControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract VolumeDialogController provideVolumeDialogController(
-            VolumeDialogControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract SensorPrivacyController provideSensorPrivacyControllerImpl(
-            SensorPrivacyControllerImpl controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract QSHost provideQsHost(QSTileHost controllerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract FalsingManager provideFalsingManager(FalsingManagerProxy falsingManagerImpl);
-
-    /**
-     */
-    @Binds
-    public abstract VolumeComponent provideVolumeComponent(
-            VolumeDialogComponent volumeDialogComponent);
-
-    /**
-     */
-    @Binds
-    public abstract RingerModeTracker provideRingerModeTracker(
-            RingerModeTrackerImpl ringerModeTrackerImpl);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 5a77723..0dd9488 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -18,6 +18,8 @@
 
 import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
 
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.app.INotificationManager;
 import android.content.Context;
 import android.content.SharedPreferences;
@@ -26,6 +28,7 @@
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.util.DisplayMetrics;
 import android.view.Choreographer;
 import android.view.IWindowManager;
@@ -39,6 +42,7 @@
 import com.android.internal.util.NotificationMessagingUtil;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.systemui.Prefs;
 import com.android.systemui.accessibility.ModeSwitchesController;
 import com.android.systemui.accessibility.SystemActions;
@@ -61,7 +65,7 @@
 import com.android.systemui.shared.plugins.PluginManagerImpl;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.phone.AutoHideController;
@@ -88,7 +92,7 @@
  * Provides dependencies for the root component of sysui injection.
  *
  * Only SystemUI owned classes and instances should go in here. Other, framework-owned classes
- * should go in {@link SystemServicesModule}.
+ * should go in {@link FrameworkServicesModule}.
  *
  * See SystemUI/docs/dagger.md
  */
@@ -163,6 +167,15 @@
 
     }
 
+    @SuppressLint("MissingPermission")
+    @SysUISingleton
+    @Provides
+    @Nullable
+    static LocalBluetoothManager provideLocalBluetoothController(Context context,
+            @Background Handler bgHandler) {
+        return LocalBluetoothManager.create(context, bgHandler, UserHandle.ALL);
+    }
+
     /** */
     @Provides
     @SysUISingleton
@@ -193,7 +206,7 @@
             SysUiState sysUiFlagsContainer,
             BroadcastDispatcher broadcastDispatcher,
             CommandQueue commandQueue,
-            Divider divider,
+            Optional<SplitScreen> splitScreenOptional,
             Optional<Recents> recentsOptional,
             Lazy<StatusBar> statusBarLazy,
             ShadeController shadeController,
@@ -215,7 +228,7 @@
                 sysUiFlagsContainer,
                 broadcastDispatcher,
                 commandQueue,
-                divider,
+                splitScreenOptional,
                 recentsOptional,
                 statusBarLazy,
                 shadeController,
@@ -321,5 +334,4 @@
     public ModeSwitchesController providesModeSwitchesController(Context context) {
         return new ModeSwitchesController(context);
     }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
rename to packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 7fe9faf..66063a8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -17,7 +17,6 @@
 package com.android.systemui.dagger;
 
 import android.annotation.Nullable;
-import android.annotation.SuppressLint;
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.AlarmManager;
@@ -41,6 +40,7 @@
 import android.hardware.SensorPrivacyManager;
 import android.hardware.display.ColorDisplayManager;
 import android.hardware.display.DisplayManager;
+import android.hardware.face.FaceManager;
 import android.media.AudioManager;
 import android.media.MediaRouter2Manager;
 import android.media.session.MediaSessionManager;
@@ -48,10 +48,8 @@
 import android.net.NetworkScoreManager;
 import android.net.wifi.WifiManager;
 import android.os.BatteryStats;
-import android.os.Handler;
 import android.os.PowerManager;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.Vibrator;
 import android.service.dreams.DreamService;
@@ -59,6 +57,7 @@
 import android.telecom.TelecomManager;
 import android.telephony.TelephonyManager;
 import android.view.IWindowManager;
+import android.view.ViewConfiguration;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityManager;
@@ -66,12 +65,12 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.util.LatencyTracker;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.DisplayId;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.shared.system.PackageManagerWrapper;
 
+import javax.inject.Singleton;
+
 import dagger.Module;
 import dagger.Provides;
 
@@ -79,51 +78,51 @@
  * Provides Non-SystemUI, Framework-Owned instances to the dependency graph.
  */
 @Module
-public class SystemServicesModule {
+public class FrameworkServicesModule {
     @Provides
-    @SysUISingleton
+    @Singleton
     static AccessibilityManager provideAccessibilityManager(Context context) {
         return context.getSystemService(AccessibilityManager.class);
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     static ActivityManager provideActivityManager(Context context) {
         return context.getSystemService(ActivityManager.class);
     }
 
-    @SysUISingleton
     @Provides
+    @Singleton
     static AlarmManager provideAlarmManager(Context context) {
         return context.getSystemService(AlarmManager.class);
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     static AudioManager provideAudioManager(Context context) {
         return context.getSystemService(AudioManager.class);
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     static ColorDisplayManager provideColorDisplayManager(Context context) {
         return context.getSystemService(ColorDisplayManager.class);
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     static ConnectivityManager provideConnectivityManagager(Context context) {
         return context.getSystemService(ConnectivityManager.class);
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     static ContentResolver provideContentResolver(Context context) {
         return context.getContentResolver();
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     static DevicePolicyManager provideDevicePolicyManager(Context context) {
         return context.getSystemService(DevicePolicyManager.class);
     }
@@ -135,45 +134,53 @@
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     static DisplayManager provideDisplayManager(Context context) {
         return context.getSystemService(DisplayManager.class);
     }
 
-    @SysUISingleton
     @Provides
+    @Singleton
     static IActivityManager provideIActivityManager() {
         return ActivityManager.getService();
     }
 
-    @SysUISingleton
     @Provides
+    @Singleton
     static IActivityTaskManager provideIActivityTaskManager() {
         return ActivityTaskManager.getService();
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     static IBatteryStats provideIBatteryStats() {
         return IBatteryStats.Stub.asInterface(
                 ServiceManager.getService(BatteryStats.SERVICE_NAME));
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     static IDreamManager provideIDreamManager() {
         return IDreamManager.Stub.asInterface(
                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
+    @Nullable
+    static FaceManager provideFaceManager(Context context) {
+        return context.getSystemService(FaceManager.class);
+
+    }
+
+    @Provides
+    @Singleton
     static IPackageManager provideIPackageManager() {
         return IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
     }
 
-    @SysUISingleton
     @Provides
+    @Singleton
     static IStatusBarService provideIStatusBarService() {
         return IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -186,39 +193,30 @@
                 ServiceManager.getService(Context.WALLPAPER_SERVICE));
     }
 
-    @SysUISingleton
     @Provides
+    @Singleton
     static IWindowManager provideIWindowManager() {
         return WindowManagerGlobal.getWindowManagerService();
     }
 
-    @SysUISingleton
     @Provides
+    @Singleton
     static KeyguardManager provideKeyguardManager(Context context) {
         return context.getSystemService(KeyguardManager.class);
     }
 
-    @SysUISingleton
     @Provides
+    @Singleton
     static LatencyTracker provideLatencyTracker(Context context) {
         return LatencyTracker.getInstance(context);
     }
 
-    @SysUISingleton
     @Provides
+    @Singleton
     static LauncherApps provideLauncherApps(Context context) {
         return context.getSystemService(LauncherApps.class);
     }
 
-    @SuppressLint("MissingPermission")
-    @SysUISingleton
-    @Provides
-    @Nullable
-    static LocalBluetoothManager provideLocalBluetoothController(Context context,
-            @Background Handler bgHandler) {
-        return LocalBluetoothManager.create(context, bgHandler, UserHandle.ALL);
-    }
-
     @Provides
     static MediaRouter2Manager provideMediaRouter2Manager(Context context) {
         return MediaRouter2Manager.getInstance(context);
@@ -230,32 +228,32 @@
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     static NetworkScoreManager provideNetworkScoreManager(Context context) {
         return context.getSystemService(NetworkScoreManager.class);
     }
 
-    @SysUISingleton
     @Provides
+    @Singleton
     static NotificationManager provideNotificationManager(Context context) {
         return context.getSystemService(NotificationManager.class);
     }
 
-    @SysUISingleton
     @Provides
+    @Singleton
     static PackageManager providePackageManager(Context context) {
         return context.getPackageManager();
     }
 
-    @SysUISingleton
     @Provides
+    @Singleton
     static PackageManagerWrapper providePackageManagerWrapper() {
         return PackageManagerWrapper.getInstance();
     }
 
     /** */
-    @SysUISingleton
     @Provides
+    @Singleton
     static PowerManager providePowerManager(Context context) {
         return context.getSystemService(PowerManager.class);
     }
@@ -267,51 +265,63 @@
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
+    static RoleManager provideRoleManager(Context context) {
+        return context.getSystemService(RoleManager.class);
+    }
+
+    @Provides
+    @Singleton
     static SensorManager providesSensorManager(Context context) {
         return context.getSystemService(SensorManager.class);
     }
 
-    @SysUISingleton
     @Provides
+    @Singleton
     static SensorPrivacyManager provideSensorPrivacyManager(Context context) {
         return context.getSystemService(SensorPrivacyManager.class);
     }
 
-    @SysUISingleton
     @Provides
+    @Singleton
     static ShortcutManager provideShortcutManager(Context context) {
         return context.getSystemService(ShortcutManager.class);
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     @Nullable
     static TelecomManager provideTelecomManager(Context context) {
         return context.getSystemService(TelecomManager.class);
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     static TelephonyManager provideTelephonyManager(Context context) {
         return context.getSystemService(TelephonyManager.class);
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     static TrustManager provideTrustManager(Context context) {
         return context.getSystemService(TrustManager.class);
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     @Nullable
     static Vibrator provideVibrator(Context context) {
         return context.getSystemService(Vibrator.class);
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
+    static ViewConfiguration provideViewConfiguration(Context context) {
+        return ViewConfiguration.get(context);
+    }
+
+    @Provides
+    @Singleton
     static UserManager provideUserManager(Context context) {
         return context.getSystemService(UserManager.class);
     }
@@ -322,21 +332,15 @@
     }
 
     @Provides
-    @SysUISingleton
+    @Singleton
     @Nullable
     static WifiManager provideWifiManager(Context context) {
         return context.getSystemService(WifiManager.class);
     }
 
-    @SysUISingleton
     @Provides
+    @Singleton
     static WindowManager provideWindowManager(Context context) {
         return context.getSystemService(WindowManager.class);
     }
-
-    @Provides
-    @SysUISingleton
-    static RoleManager provideRoleManager(Context context) {
-        return context.getSystemService(RoleManager.class);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
index 553655b..c5dc8cc 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java
@@ -16,63 +16,27 @@
 
 package com.android.systemui.dagger;
 
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.hardware.display.AmbientDisplayConfiguration;
-import android.util.DisplayMetrics;
-import android.view.Choreographer;
-
-import com.android.systemui.Prefs;
-import com.android.systemui.dagger.qualifiers.Main;
-
-import javax.inject.Singleton;
+import com.android.systemui.util.concurrency.GlobalConcurrencyModule;
 
 import dagger.Module;
-import dagger.Provides;
 
 /**
- * Supplies globally scoped instances.
+ * Supplies globally scoped instances that should be available in all versions of SystemUI
  *
  * Providers in this module will be accessible to both WMComponent and SysUIComponent scoped
  * classes. They are in here because they are either needed globally or are inherently universal
  * to the application.
  *
  * Note that just because a class might be used by both WM and SysUI does not necessarily mean that
- * it should got into this module. If WM and SysUI might need the class for different purposes
+ * it should go into this module. If WM and SysUI might need the class for different purposes
  * or different semantics, it may make sense to ask them to supply their own. Something like
  * threading and concurrency provide a good example. Both components need
  * Threads/Handlers/Executors, but they need separate instances of them in many cases.
  *
  * Please use discretion when adding things to the global scope.
  */
-@Module
+@Module(includes = {
+        FrameworkServicesModule.class,
+        GlobalConcurrencyModule.class})
 public class GlobalModule {
-    /** */
-    @Provides
-    @Main
-    public SharedPreferences provideSharePreferences(Context context) {
-        return Prefs.get(context);
-    }
-
-    /** */
-    @Provides
-    public AmbientDisplayConfiguration provideAmbientDisplayConfiguration(Context context) {
-        return new AmbientDisplayConfiguration(context);
-    }
-
-    /** */
-    @Provides
-    @Singleton
-    public Choreographer providesChoreographer() {
-        return Choreographer.getInstance();
-    }
-
-    /** */
-    @Provides
-    @Singleton
-    public DisplayMetrics provideDisplayMetrics(Context context) {
-        DisplayMetrics displayMetrics = new DisplayMetrics();
-        context.getDisplay().getMetrics(displayMetrics);
-        return displayMetrics;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
index 3d7c8ad4..00fdf55 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 
+import com.android.systemui.util.concurrency.ThreadFactory;
+
 import javax.inject.Singleton;
 
 import dagger.BindsInstance;
@@ -28,6 +30,7 @@
  */
 @Singleton
 @Component(modules = {
+        GlobalModule.class,
         SysUISubcomponentModule.class,
         WMModule.class})
 public interface GlobalRootComponent {
@@ -52,4 +55,9 @@
      * Builder for a SysuiComponent.
      */
     SysUIComponent.Builder getSysUIComponent();
+
+    /**
+     * Build a {@link ThreadFactory}.
+     */
+    ThreadFactory createThreadFactory();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java b/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java
new file mode 100644
index 0000000..406981d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/PluginModule.java
@@ -0,0 +1,74 @@
+/*
+ * 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.dagger;
+
+import com.android.systemui.ActivityStarterDelegate;
+import com.android.systemui.classifier.FalsingManagerProxy;
+import com.android.systemui.globalactions.GlobalActionsComponent;
+import com.android.systemui.globalactions.GlobalActionsImpl;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.GlobalActions;
+import com.android.systemui.plugins.VolumeDialogController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl;
+import com.android.systemui.volume.VolumeDialogControllerImpl;
+
+import dagger.Binds;
+import dagger.Module;
+
+/**
+ * Module for binding Plugin implementations.
+ *
+ * TODO(b/166258224): Many of these should be moved closer to their implementations.
+ */
+@Module
+public interface PluginModule {
+
+    /** */
+    @Binds
+    ActivityStarter provideActivityStarter(ActivityStarterDelegate delegate);
+
+    /** */
+    @Binds
+    DarkIconDispatcher provideDarkIconDispatcher(DarkIconDispatcherImpl controllerImpl);
+
+    /** */
+    @Binds
+    FalsingManager provideFalsingManager(FalsingManagerProxy falsingManagerImpl);
+
+    /** */
+    @Binds
+    GlobalActions provideGlobalActions(GlobalActionsImpl controllerImpl);
+
+    /** */
+    @Binds
+    GlobalActions.GlobalActionsManager provideGlobalActionsManager(
+            GlobalActionsComponent controllerImpl);
+
+    /** */
+    @Binds
+    StatusBarStateController provideStatusBarStateController(
+            StatusBarStateControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    VolumeDialogController provideVolumeDialogController(VolumeDialogControllerImpl controllerImpl);
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index b606201..2622593 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -26,6 +26,7 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.InjectionInflationController;
 
+import dagger.BindsInstance;
 import dagger.Subcomponent;
 
 /**
@@ -35,9 +36,7 @@
 @Subcomponent(modules = {
         DefaultComponentBinder.class,
         DependencyProvider.class,
-        DependencyBinder.class,
         PipModule.class,
-        SystemServicesModule.class,
         SystemUIBinder.class,
         SystemUIModule.class,
         SystemUIDefaultModule.class})
@@ -48,6 +47,9 @@
      */
     @Subcomponent.Builder
     interface Builder {
+        @BindsInstance
+        Builder setStubAPIClass(WMComponent.StubAPIClass stubAPIClass);
+
         SysUIComponent build();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 0aebb7e..9dfd9f8 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -34,7 +34,6 @@
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsModule;
 import com.android.systemui.shortcut.ShortcutKeyDispatcher;
-import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.dagger.StatusBarModule;
 import com.android.systemui.statusbar.notification.InstantAppNotifier;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -43,6 +42,7 @@
 import com.android.systemui.toast.ToastUI;
 import com.android.systemui.util.leak.GarbageMonitor;
 import com.android.systemui.volume.VolumeUI;
+import com.android.systemui.wmshell.WMShell;
 
 import dagger.Binds;
 import dagger.Module;
@@ -61,12 +61,6 @@
     @ClassKey(AuthController.class)
     public abstract SystemUI bindAuthController(AuthController service);
 
-    /** Inject into Divider. */
-    @Binds
-    @IntoMap
-    @ClassKey(Divider.class)
-    public abstract SystemUI bindDivider(Divider sysui);
-
     /** Inject into GarbageMonitor.Service. */
     @Binds
     @IntoMap
@@ -187,4 +181,10 @@
     @IntoMap
     @ClassKey(WindowMagnification.class)
     public abstract SystemUI bindWindowMagnification(WindowMagnification sysui);
+
+    /** Inject into WMShell. */
+    @Binds
+    @IntoMap
+    @ClassKey(WMShell.class)
+    public abstract SystemUI bindWMShell(WMShell sysui);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index 3b225d5..a021114 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -41,7 +41,6 @@
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImplementation;
-import com.android.systemui.stackdivider.DividerModule;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
@@ -75,7 +74,6 @@
  * overridden by the System UI implementation.
  */
 @Module(includes = {
-            DividerModule.class,
             QSModule.class,
             WMShellModule.class
         })
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index abfc2ab..99a3a91 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -16,24 +16,20 @@
 
 package com.android.systemui.dagger;
 
-import android.annotation.Nullable;
-import android.content.Context;
-import android.content.pm.PackageManager;
-
-import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.BootCompleteCache;
 import com.android.systemui.BootCompleteCacheImpl;
+import com.android.systemui.appops.dagger.AppOpsModule;
 import com.android.systemui.assist.AssistModule;
+import com.android.systemui.controls.dagger.ControlsModule;
 import com.android.systemui.demomode.dagger.DemoModeModule;
 import com.android.systemui.doze.dagger.DozeComponent;
-import com.android.systemui.dump.DumpManager;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.log.dagger.LogModule;
 import com.android.systemui.model.SysUiState;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.power.dagger.PowerModule;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.screenshot.dagger.ScreenshotModule;
 import com.android.systemui.settings.dagger.SettingsModule;
-import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
@@ -41,16 +37,18 @@
 import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
-import com.android.systemui.statusbar.phone.KeyguardLiftController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
-import com.android.systemui.util.concurrency.ConcurrencyModule;
-import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.statusbar.policy.dagger.StatusBarPolicyModule;
+import com.android.systemui.tuner.dagger.TunerModule;
+import com.android.systemui.util.concurrency.SysUIConcurrencyModule;
+import com.android.systemui.util.dagger.UtilModule;
 import com.android.systemui.util.sensors.SensorModule;
 import com.android.systemui.util.settings.SettingsUtilModule;
 import com.android.systemui.util.time.SystemClock;
 import com.android.systemui.util.time.SystemClockImpl;
+import com.android.systemui.volume.dagger.VolumeModule;
 
 import dagger.Binds;
 import dagger.BindsOptionalOf;
@@ -62,14 +60,23 @@
  * implementation.
  */
 @Module(includes = {
+            AppOpsModule.class,
             AssistModule.class,
-            ConcurrencyModule.class,
+            ControlsModule.class,
             DemoModeModule.class,
             LogModule.class,
             PeopleHubModule.class,
+            PowerModule.class,
+            PluginModule.class,
+            ScreenshotModule.class,
             SensorModule.class,
             SettingsModule.class,
-            SettingsUtilModule.class
+            SettingsUtilModule.class,
+            StatusBarPolicyModule.class,
+            SysUIConcurrencyModule.class,
+            TunerModule.class,
+            UtilModule.class,
+            VolumeModule.class
         },
         subcomponents = {StatusBarComponent.class,
                 NotificationRowComponent.class,
@@ -87,22 +94,6 @@
     public abstract ContextComponentHelper bindComponentHelper(
             ContextComponentResolver componentHelper);
 
-    @SysUISingleton
-    @Provides
-    @Nullable
-    static KeyguardLiftController provideKeyguardLiftController(
-            Context context,
-            StatusBarStateController statusBarStateController,
-            AsyncSensorManager asyncSensorManager,
-            KeyguardUpdateMonitor keyguardUpdateMonitor,
-            DumpManager dumpManager) {
-        if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
-            return null;
-        }
-        return new KeyguardLiftController(statusBarStateController, asyncSensorManager,
-                keyguardUpdateMonitor, dumpManager);
-    }
-
     /** */
     @Binds
     public abstract NotificationRowBinder bindNotificationRowBinder(
@@ -118,9 +109,6 @@
     abstract CommandQueue optionalCommandQueue();
 
     @BindsOptionalOf
-    abstract Divider optionalDivider();
-
-    @BindsOptionalOf
     abstract HeadsUpManager optionalHeadsUpManager();
 
     @BindsOptionalOf
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index 929b61a..ad90eff 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.dagger;
 
+import javax.inject.Inject;
+
 import dagger.Subcomponent;
 
 /**
@@ -32,4 +34,19 @@
     interface Builder {
         WMComponent build();
     }
+
+
+    /**
+     *  Example class used for passing an API to SysUI from WMShell.
+     *
+     *  TODO: Remove this once real WM classes are ready to go.
+     **/
+    @WMSingleton
+    class StubAPIClass {
+        @Inject
+        StubAPIClass() {}
+    }
+
+    /** Create a StubAPIClass. */
+    StubAPIClass createStubAPIClass();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index e38dce0..8364b48 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -262,11 +262,11 @@
             onWakeScreen(wakeEvent, mMachine.isExecutingTransition() ? null : mMachine.getState());
         } else if (isLongPress) {
             requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
-                    null /* onPulseSupressedListener */);
+                    null /* onPulseSuppressedListener */);
         } else if (isWakeLockScreen) {
             if (wakeEvent) {
                 requestPulse(pulseReason, true /* alreadyPerformedProxCheck */,
-                        null /* onPulseSupressedListener */);
+                        null /* onPulseSuppressedListener */);
             }
         } else {
             proximityCheckThenCall((result) -> {
@@ -536,7 +536,7 @@
             if (PULSE_ACTION.equals(intent.getAction())) {
                 if (DozeMachine.DEBUG) Log.d(TAG, "Received pulse intent");
                 requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */
-                        null /* onPulseSupressedListener */);
+                        null /* onPulseSuppressedListener */);
             }
             if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
                 mMachine.requestState(DozeMachine.State.FINISH);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index d213ac1..3e64749 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -130,7 +130,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -252,7 +252,7 @@
     private final RingerModeTracker mRingerModeTracker;
     private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
     private Handler mMainHandler;
-    private CurrentUserContextTracker mCurrentUserContextTracker;
+    private UserContextProvider mUserContextProvider;
     @VisibleForTesting
     boolean mShowLockScreenCardsAndControls = false;
 
@@ -313,7 +313,7 @@
             UiEventLogger uiEventLogger,
             RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler,
             ControlsComponent controlsComponent,
-            CurrentUserContextTracker currentUserContextTracker) {
+            UserContextProvider userContextProvider) {
         mContext = context;
         mWindowManagerFuncs = windowManagerFuncs;
         mAudioManager = audioManager;
@@ -342,7 +342,7 @@
         mControlsControllerOptional = controlsComponent.getControlsController();
         mSysUiState = sysUiState;
         mMainHandler = handler;
-        mCurrentUserContextTracker = currentUserContextTracker;
+        mUserContextProvider = userContextProvider;
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -436,7 +436,7 @@
         String[] preferredControlsPackages = mContext.getResources()
                 .getStringArray(com.android.systemui.R.array.config_controlsPreferredPackages);
 
-        SharedPreferences prefs = mCurrentUserContextTracker.getCurrentUserContext()
+        SharedPreferences prefs = mUserContextProvider.getUserContext()
                 .getSharedPreferences(PREFS_CONTROLS_FILE, Context.MODE_PRIVATE);
         Set<String> seededPackages = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED,
                 Collections.emptySet());
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt
new file mode 100644
index 0000000..9dcc3bb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessController.kt
@@ -0,0 +1,193 @@
+/*
+ * 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.keyguard
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.content.res.Resources
+import android.database.ContentObserver
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.hardware.biometrics.BiometricSourceType
+import android.os.Handler
+import android.provider.Settings.System.SCREEN_BRIGHTNESS_FLOAT
+import android.util.MathUtils
+import android.view.View
+import android.view.WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE
+import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+import com.android.internal.annotations.VisibleForTesting
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SystemSettings
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.lang.Float.max
+import java.util.concurrent.TimeUnit
+
+val DEFAULT_ANIMATION_DURATION = TimeUnit.SECONDS.toMillis(4)
+val MAX_SCREEN_BRIGHTNESS = 100 // 0..100
+val MAX_SCRIM_OPACTY = 50 // 0..100
+val DEFAULT_USE_FACE_WALLPAPER = false
+
+/**
+ * This class is responsible for ramping up the display brightness (and white overlay) in order
+ * to mitigate low light conditions when running face auth without an IR camera.
+ */
+@SysUISingleton
+open class FaceAuthScreenBrightnessController(
+    private val notificationShadeWindowController: NotificationShadeWindowController,
+    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+    private val resources: Resources,
+    private val globalSettings: GlobalSettings,
+    private val systemSettings: SystemSettings,
+    private val mainHandler: Handler,
+    private val dumpManager: DumpManager
+) : Dumpable {
+
+    private var userDefinedBrightness: Float = 1f
+    @VisibleForTesting
+    var useFaceAuthWallpaper = globalSettings
+            .getInt("sysui.use_face_auth_wallpaper", if (DEFAULT_USE_FACE_WALLPAPER) 1 else 0) == 1
+    private val brightnessAnimationDuration = globalSettings
+            .getLong("sysui.face_brightness_anim_duration", DEFAULT_ANIMATION_DURATION)
+    private val maxScreenBrightness = globalSettings
+            .getInt("sysui.face_max_brightness", MAX_SCREEN_BRIGHTNESS) / 100f
+    private val maxScrimOpacity = globalSettings
+            .getInt("sysui.face_max_scrim_opacity", MAX_SCRIM_OPACTY) / 100f
+    private val keyguardUpdateCallback = object : KeyguardUpdateMonitorCallback() {
+        override fun onBiometricRunningStateChanged(
+            running: Boolean,
+            biometricSourceType: BiometricSourceType?
+        ) {
+            if (biometricSourceType != BiometricSourceType.FACE) {
+                return
+            }
+            // TODO enable only when receiving a low-light error
+            overridingBrightness = running
+        }
+    }
+    private lateinit var whiteOverlay: View
+    private var brightnessAnimator: ValueAnimator? = null
+    private var overridingBrightness = false
+    set(value) {
+        if (field == value) {
+            return
+        }
+        field = value
+        brightnessAnimator?.cancel()
+
+        if (!value) {
+            notificationShadeWindowController.setFaceAuthDisplayBrightness(BRIGHTNESS_OVERRIDE_NONE)
+            if (whiteOverlay.alpha > 0) {
+                brightnessAnimator = createAnimator(whiteOverlay.alpha, 0f).apply {
+                    duration = 200
+                    addUpdateListener {
+                        whiteOverlay.alpha = it.animatedValue as Float
+                    }
+                    addListener(object : AnimatorListenerAdapter() {
+                        override fun onAnimationEnd(animation: Animator?) {
+                            whiteOverlay.visibility = View.INVISIBLE
+                            brightnessAnimator = null
+                        }
+                    })
+                    start()
+                }
+            }
+            return
+        }
+
+        val targetBrightness = max(maxScreenBrightness, userDefinedBrightness)
+        whiteOverlay.visibility = View.VISIBLE
+        brightnessAnimator = createAnimator(0f, 1f).apply {
+            duration = brightnessAnimationDuration
+            addUpdateListener {
+                val progress = it.animatedValue as Float
+                val brightnessProgress = MathUtils.constrainedMap(
+                        userDefinedBrightness, targetBrightness, 0f, 0.5f, progress)
+                val scrimProgress = MathUtils.constrainedMap(
+                        0f, maxScrimOpacity, 0.5f, 1f, progress)
+                notificationShadeWindowController.setFaceAuthDisplayBrightness(brightnessProgress)
+                whiteOverlay.alpha = scrimProgress
+            }
+            addListener(object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator?) {
+                    brightnessAnimator = null
+                }
+            })
+            start()
+        }
+    }
+
+    @VisibleForTesting
+    open fun createAnimator(start: Float, end: Float) = ValueAnimator.ofFloat(start, end)
+
+    /**
+     * Returns a bitmap that should be used by the lock screen as a wallpaper, if face auth requires
+     * a secure wallpaper.
+     */
+    var faceAuthWallpaper: Bitmap? = null
+    get() {
+        val user = KeyguardUpdateMonitor.getCurrentUser()
+        if (useFaceAuthWallpaper && keyguardUpdateMonitor.isFaceAuthEnabledForUser(user)) {
+            val options = BitmapFactory.Options().apply {
+                inScaled = false
+            }
+            return BitmapFactory.decodeResource(resources, R.drawable.face_auth_wallpaper, options)
+        }
+        return null
+    }
+    private set
+
+    fun attach(overlayView: View) {
+        whiteOverlay = overlayView
+        whiteOverlay.focusable = FLAG_NOT_FOCUSABLE
+        whiteOverlay.background = ColorDrawable(Color.WHITE)
+        whiteOverlay.isEnabled = false
+        whiteOverlay.alpha = 0f
+        whiteOverlay.visibility = View.INVISIBLE
+
+        dumpManager.registerDumpable(this.javaClass.name, this)
+        keyguardUpdateMonitor.registerCallback(keyguardUpdateCallback)
+        systemSettings.registerContentObserver(SCREEN_BRIGHTNESS_FLOAT,
+            object : ContentObserver(mainHandler) {
+                override fun onChange(selfChange: Boolean) {
+                    userDefinedBrightness = systemSettings.getFloat(SCREEN_BRIGHTNESS_FLOAT)
+                }
+            })
+        userDefinedBrightness = systemSettings.getFloat(SCREEN_BRIGHTNESS_FLOAT)
+    }
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+        pw.apply {
+            println("overridingBrightness: $overridingBrightness")
+            println("useFaceAuthWallpaper: $useFaceAuthWallpaper")
+            println("brightnessAnimator: $brightnessAnimator")
+            println("brightnessAnimationDuration: $brightnessAnimationDuration")
+            println("maxScreenBrightness: $maxScreenBrightness")
+            println("userDefinedBrightness: $userDefinedBrightness")
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 6214a64..3340791 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -89,15 +89,14 @@
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.dagger.KeyguardModule;
+import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.DeviceConfigProxy;
-import com.android.systemui.util.InjectionInflationController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -228,7 +227,6 @@
 
     /** TrustManager for letting it know when we change visibility */
     private final TrustManager mTrustManager;
-    private final InjectionInflationController mInjectionInflationController;
 
     /**
      * Used to keep the device awake while to ensure the keyguard finishes opening before
@@ -345,7 +343,7 @@
     /**
      * For managing external displays
      */
-    private KeyguardDisplayManager mKeyguardDisplayManager;
+    private final KeyguardDisplayManager mKeyguardDisplayManager;
 
     private final ArrayList<IKeyguardStateCallback> mKeyguardStateCallbacks = new ArrayList<>();
 
@@ -724,7 +722,7 @@
             TrustManager trustManager,
             DeviceConfigProxy deviceConfig,
             NavigationModeController navigationModeController,
-            InjectionInflationController injectionInflationController) {
+            KeyguardDisplayManager keyguardDisplayManager) {
         super(context);
         mFalsingManager = falsingManager;
         mLockPatternUtils = lockPatternUtils;
@@ -735,7 +733,7 @@
         mUpdateMonitor = keyguardUpdateMonitor;
         mPM = powerManager;
         mTrustManager = trustManager;
-        mInjectionInflationController = injectionInflationController;
+        mKeyguardDisplayManager = keyguardDisplayManager;
         dumpManager.registerDumpable(getClass().getName(), this);
         mDeviceConfig = deviceConfig;
         mShowHomeOverLockscreen = mDeviceConfig.getBoolean(
@@ -775,9 +773,6 @@
         mContext.registerReceiver(mDelayedLockBroadcastReceiver, delayedActionFilter,
                 SYSTEMUI_PERMISSION, null /* scheduler */);
 
-        mKeyguardDisplayManager = new KeyguardDisplayManager(mContext,
-                mInjectionInflationController);
-
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
 
         KeyguardUpdateMonitor.setCurrentUser(ActivityManager.getCurrentUser());
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 7c5dcd8..9d8e73a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -16,25 +16,40 @@
 
 package com.android.systemui.keyguard.dagger;
 
+import android.annotation.Nullable;
 import android.app.trust.TrustManager;
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.hardware.face.FaceManager;
+import android.os.Handler;
 import android.os.PowerManager;
 
 import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardDisplayManager;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardViewController;
+import com.android.keyguard.dagger.KeyguardStatusViewComponent;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.KeyguardLiftController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.DeviceConfigProxy;
-import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.settings.SystemSettings;
 
+import java.util.Optional;
 import java.util.concurrent.Executor;
 
 import dagger.Lazy;
@@ -44,7 +59,7 @@
 /**
  * Dagger Module providing {@link StatusBar}.
  */
-@Module
+@Module(subcomponents = {KeyguardStatusViewComponent.class})
 public class KeyguardModule {
     /**
      * Provides our instance of KeyguardViewMediator which is considered optional.
@@ -65,7 +80,7 @@
             @UiBackground Executor uiBgExecutor,
             DeviceConfigProxy deviceConfig,
             NavigationModeController navigationModeController,
-            InjectionInflationController injectionInflationController) {
+            KeyguardDisplayManager keyguardDisplayManager) {
         return new KeyguardViewMediator(
                 context,
                 falsingManager,
@@ -80,6 +95,53 @@
                 trustManager,
                 deviceConfig,
                 navigationModeController,
-                injectionInflationController);
+                keyguardDisplayManager
+        );
+    }
+
+    @SysUISingleton
+    @Provides
+    @Nullable
+    static KeyguardLiftController provideKeyguardLiftController(
+            Context context,
+            StatusBarStateController statusBarStateController,
+            AsyncSensorManager asyncSensorManager,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            DumpManager dumpManager) {
+        if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
+            return null;
+        }
+        return new KeyguardLiftController(statusBarStateController, asyncSensorManager,
+                keyguardUpdateMonitor, dumpManager);
+    }
+
+    @SysUISingleton
+    @Provides
+    static Optional<FaceAuthScreenBrightnessController> provideFaceAuthScreenBrightnessController(
+            Context context,
+            NotificationShadeWindowController notificationShadeWindowController,
+            @Main Resources resources,
+            Handler handler,
+            @Nullable FaceManager faceManager,
+            PackageManager packageManager,
+            KeyguardUpdateMonitor keyguardUpdateMonitor,
+            GlobalSettings globalSetting,
+            SystemSettings systemSettings,
+            DumpManager dumpManager) {
+        if (faceManager == null || !packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+            return Optional.empty();
+        }
+
+        // Cameras that support "self illumination," via IR for example, don't need low light
+        // environment mitigation.
+        boolean needsLowLightMitigation = faceManager.getSensorProperties().stream()
+                .anyMatch((properties) -> !properties.supportsSelfIllumination);
+        if (!needsLowLightMitigation) {
+            return Optional.empty();
+        }
+
+        return Optional.of(new FaceAuthScreenBrightnessController(
+                notificationShadeWindowController, keyguardUpdateMonitor, resources,
+                globalSetting, systemSettings, handler, dumpManager));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index a003d83..e5a9ac1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -172,7 +172,6 @@
                     // This view is inactive, let's remove this! This happens e.g when dismissing /
                     // timing out a view. We still have the data around because resumption could
                     // be on, but we should save the resources and release this.
-                    oldKey?.let { MediaPlayerData.removeMediaPlayer(it) }
                     onMediaDataRemoved(key)
                 } else {
                     addOrUpdatePlayer(key, oldKey, data)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
index 77cac50..4863999 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.qs.PageIndicator
 import com.android.systemui.R
+import com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.util.animation.PhysicsAnimator
 import com.android.systemui.util.concurrency.DelayableExecutor
@@ -315,7 +316,8 @@
         return false
     }
 
-    private fun isFalseTouch() = falsingProtectionNeeded && falsingManager.isFalseTouch
+    private fun isFalseTouch() = falsingProtectionNeeded &&
+            falsingManager.isFalseTouch(NOTIFICATION_DISMISS)
 
     private fun getMaxTranslation() = if (showsSettingsButton) {
             settingsButton.width
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
index fdbff98..6bd5274 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt
@@ -54,32 +54,35 @@
         if (mediaListeners.containsKey(key)) {
             return
         }
-        // Having an old key means that we're migrating from/to resumption. We should invalidate
-        // the old listener and create a new one.
+        // Having an old key means that we're migrating from/to resumption. We should update
+        // the old listener to make sure that events will be dispatched to the new location.
         val migrating = oldKey != null && key != oldKey
         var wasPlaying = false
         if (migrating) {
-            if (mediaListeners.containsKey(oldKey)) {
-                val oldListener = mediaListeners.remove(oldKey)
-                wasPlaying = oldListener?.playing ?: false
-                oldListener?.destroy()
+            val reusedListener = mediaListeners.remove(oldKey)
+            if (reusedListener != null) {
+                wasPlaying = reusedListener.playing ?: false
                 if (DEBUG) Log.d(TAG, "migrating key $oldKey to $key, for resumption")
+                reusedListener.mediaData = data
+                reusedListener.key = key
+                mediaListeners[key] = reusedListener
+                if (wasPlaying != reusedListener.playing) {
+                    // If a player becomes active because of a migration, we'll need to broadcast
+                    // its state. Doing it now would lead to reentrant callbacks, so let's wait
+                    // until we're done.
+                    mainExecutor.execute {
+                        if (mediaListeners[key]?.playing == true) {
+                            if (DEBUG) Log.d(TAG, "deliver delayed playback state for $key")
+                            timeoutCallback.invoke(key, false /* timedOut */)
+                        }
+                    }
+                }
+                return
             } else {
                 Log.w(TAG, "Old key $oldKey for player $key doesn't exist. Continuing...")
             }
         }
         mediaListeners[key] = PlaybackStateListener(key, data)
-
-        // If a player becomes active because of a migration, we'll need to broadcast its state.
-        // Doing it now would lead to reentrant callbacks, so let's wait until we're done.
-        if (migrating && mediaListeners[key]?.playing != wasPlaying) {
-            mainExecutor.execute {
-                if (mediaListeners[key]?.playing == true) {
-                    if (DEBUG) Log.d(TAG, "deliver delayed playback state for $key")
-                    timeoutCallback.invoke(key, false /* timedOut */)
-                }
-            }
-        }
     }
 
     override fun onMediaDataRemoved(key: String) {
@@ -91,26 +94,34 @@
     }
 
     private inner class PlaybackStateListener(
-        private val key: String,
+        var key: String,
         data: MediaData
     ) : MediaController.Callback() {
 
         var timedOut = false
         var playing: Boolean? = null
 
+        var mediaData: MediaData = data
+            set(value) {
+                mediaController?.unregisterCallback(this)
+                field = value
+                mediaController = if (field.token != null) {
+                    mediaControllerFactory.create(field.token)
+                } else {
+                    null
+                }
+                mediaController?.registerCallback(this)
+                // Let's register the cancellations, but not dispatch events now.
+                // Timeouts didn't happen yet and reentrant events are troublesome.
+                processState(mediaController?.playbackState, dispatchEvents = false)
+            }
+
         // Resume controls may have null token
-        private val mediaController = if (data.token != null) {
-            mediaControllerFactory.create(data.token)
-        } else {
-            null
-        }
+        private var mediaController: MediaController? = null
         private var cancellation: Runnable? = null
 
         init {
-            mediaController?.registerCallback(this)
-            // Let's register the cancellations, but not dispatch events now.
-            // Timeouts didn't happen yet and reentrant events are troublesome.
-            processState(mediaController?.playbackState, dispatchEvents = false)
+            mediaData = data
         }
 
         fun destroy() {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 6c8a23b..c7e7817 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -111,7 +111,6 @@
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
 import com.android.systemui.navigationbar.buttons.KeyButtonView;
@@ -123,7 +122,7 @@
 import com.android.systemui.recents.Recents;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.AutoHideUiElement;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
@@ -180,7 +179,7 @@
     private final NavigationModeController mNavigationModeController;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final CommandQueue mCommandQueue;
-    private final Divider mDivider;
+    private final Optional<SplitScreen> mSplitScreenOptional;
     private final Optional<Recents> mRecentsOptional;
     private final SystemActions mSystemActions;
     private final Handler mHandler;
@@ -377,22 +376,22 @@
 
     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);
-            }
-        }
-    };
+                @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);
+                    }
+                }
+            };
 
     private final DeviceProvisionedController.DeviceProvisionedListener mUserSetupListener =
             new DeviceProvisionedController.DeviceProvisionedListener() {
-        @Override
-        public void onUserSetupChanged() {
-            mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
-        }
-    };
+                @Override
+                public void onUserSetupChanged() {
+                    mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
+                }
+            };
 
     public NavigationBar(Context context,
             WindowManager windowManager,
@@ -406,7 +405,8 @@
             StatusBarStateController statusBarStateController,
             SysUiState sysUiFlagsContainer,
             BroadcastDispatcher broadcastDispatcher,
-            CommandQueue commandQueue, Divider divider,
+            CommandQueue commandQueue,
+            Optional<SplitScreen> splitScreenOptional,
             Optional<Recents> recentsOptional, Lazy<StatusBar> statusBarLazy,
             ShadeController shadeController,
             NotificationRemoteInputManager notificationRemoteInputManager,
@@ -430,7 +430,7 @@
         mNavBarMode = navigationModeController.addListener(this);
         mBroadcastDispatcher = broadcastDispatcher;
         mCommandQueue = commandQueue;
-        mDivider = divider;
+        mSplitScreenOptional = splitScreenOptional;
         mRecentsOptional = recentsOptional;
         mSystemActions = systemActions;
         mHandler = mainHandler;
@@ -458,10 +458,10 @@
         lp.windowAnimations = 0;
         lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
 
-        LayoutInflater layoutInflater = LayoutInflater.from(mContext);
-        NavigationBarFrame frame = (NavigationBarFrame) layoutInflater.inflate(
+        NavigationBarFrame frame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate(
                 R.layout.navigation_bar_window, null);
-        View barView = layoutInflater.inflate(R.layout.navigation_bar, frame);
+        View barView = LayoutInflater.from(frame.getContext()).inflate(
+                R.layout.navigation_bar, frame);
         barView.addOnAttachStateChangeListener(this);
 
         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView);
@@ -528,6 +528,7 @@
         }
         mNavigationBarView.setNavigationIconHints(mNavigationIconHints);
         mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
+        mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
 
         prepareNavigationBarView();
         checkNavBarModes();
@@ -559,7 +560,7 @@
         setDisabled2Flags(mDisabledFlags2);
         if (mIsOnDefaultDisplay) {
             mAssistHandlerViewController =
-                new AssistHandleViewController(mHandler, mNavigationBarView);
+                    new AssistHandleViewController(mHandler, mNavigationBarView);
             getBarTransitions().addDarkIntensityListener(mAssistHandlerViewController);
         }
 
@@ -661,7 +662,7 @@
                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                         | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                         | WindowManager.LayoutParams.FLAG_SLIPPERY,
                 PixelFormat.TRANSLUCENT);
         mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId());
@@ -690,7 +691,8 @@
             return;
         }
 
-        if (mStartingQuickSwitchRotation == -1 || mDivider.isDividerVisible()) {
+        if (mStartingQuickSwitchRotation == -1 || mSplitScreenOptional
+                .map(SplitScreen::isDividerVisible).orElse(false)) {
             // Hide the secondary home handle if we are in multiwindow since apps in multiwindow
             // aren't allowed to set the display orientation
             resetSecondaryHandle();
@@ -1121,7 +1123,7 @@
         }
         mMetricsLogger.action(MetricsEvent.ACTION_ASSIST_LONG_PRESS);
         mUiEventLogger.log(NavBarActionEvent.NAVBAR_ASSIST_LONGPRESS);
-        Bundle args  = new Bundle();
+        Bundle args = new Bundle();
         args.putInt(
                 AssistManager.INVOCATION_TYPE_KEY, AssistManager.INVOCATION_HOME_BUTTON_LONG_PRESS);
         mAssistManagerLazy.get().startAssist(args);
@@ -1246,10 +1248,12 @@
 
     private boolean onLongPressRecents() {
         if (mRecentsOptional.isPresent() || !ActivityTaskManager.supportsMultiWindow(mContext)
-                || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible()
                 || ActivityManager.isLowRamDeviceStatic()
                 // If we are connected to the overview service, then disable the recents button
-                || mOverviewProxyService.getProxy() != null) {
+                || mOverviewProxyService.getProxy() != null
+                || !mSplitScreenOptional.map(splitScreen ->
+                splitScreen.getDividerView().getSnapAlgorithm().isSplitScreenFeasible())
+                .orElse(false)) {
             return false;
         }
 
@@ -1311,6 +1315,7 @@
 
     /**
      * Returns the system UI flags corresponding the the current accessibility button state
+     *
      * @param outFeedbackEnabled if non-null, sets it to true if accessibility feedback is enabled.
      */
     public int getA11yButtonState(@Nullable boolean[] outFeedbackEnabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index fd157c6..9b9dc6d 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -55,7 +55,7 @@
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -96,7 +96,7 @@
     private final SysUiState mSysUiFlagsContainer;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final CommandQueue mCommandQueue;
-    private final Divider mDivider;
+    private final Optional<SplitScreen> mSplitScreenOptional;
     private final Optional<Recents> mRecentsOptional;
     private final Lazy<StatusBar> mStatusBarLazy;
     private final ShadeController mShadeController;
@@ -130,7 +130,7 @@
             SysUiState sysUiFlagsContainer,
             BroadcastDispatcher broadcastDispatcher,
             CommandQueue commandQueue,
-            Divider divider,
+            Optional<SplitScreen> splitScreenOptional,
             Optional<Recents> recentsOptional,
             Lazy<StatusBar> statusBarLazy,
             ShadeController shadeController,
@@ -152,7 +152,7 @@
         mSysUiFlagsContainer = sysUiFlagsContainer;
         mBroadcastDispatcher = broadcastDispatcher;
         mCommandQueue = commandQueue;
-        mDivider = divider;
+        mSplitScreenOptional = splitScreenOptional;
         mRecentsOptional = recentsOptional;
         mStatusBarLazy = statusBarLazy;
         mShadeController = shadeController;
@@ -278,7 +278,7 @@
                 mSysUiFlagsContainer,
                 mBroadcastDispatcher,
                 mCommandQueue,
-                mDivider,
+                mSplitScreenOptional,
                 mRecentsOptional,
                 mStatusBarLazy,
                 mShadeController,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index a335183..8a468f6e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -87,14 +87,13 @@
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.LightBarTransitionsController;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
 import com.android.systemui.statusbar.phone.StatusBar;
 
-import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.function.Consumer;
 
@@ -329,8 +328,8 @@
         mContextualButtonGroup.addButton(accessibilityButton);
 
         mOverviewProxyService = Dependency.get(OverviewProxyService.class);
-        mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
         mFloatingRotationButton = new FloatingRotationButton(context);
+        mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
         mRotationButtonController = new RotationButtonController(mLightContext,
                 mLightIconColor, mDarkIconColor,
                 isGesturalMode ? mFloatingRotationButton : rotateSuggestionButton,
@@ -912,9 +911,6 @@
         mNavigationInflaterView.setButtonDispatchers(mButtonDispatchers);
 
         getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
-
-        Divider divider = Dependency.get(Divider.class);
-        divider.registerInSplitScreenListener(mDockedListener);
         updateOrientationViews();
         reloadNavIcons();
     }
@@ -1287,6 +1283,10 @@
         return super.onApplyWindowInsets(insets);
     }
 
+    void registerDockedListener(SplitScreen splitScreen) {
+        splitScreen.registerInSplitScreenListener(mDockedListener);
+    }
+
     private static void dumpButton(PrintWriter pw, String caption, ButtonDispatcher button) {
         pw.print("      " + caption + ": ");
         if (button == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index cfc5447..c956702 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -49,6 +49,9 @@
 import android.util.Log;
 import android.util.Size;
 import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.View;
+import android.view.WindowManager;
 import android.window.TaskOrganizer;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
@@ -57,9 +60,11 @@
 
 import com.android.internal.os.SomeArgs;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.pip.phone.PipMenuActivityController;
 import com.android.systemui.pip.phone.PipUpdateThread;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 
 import java.io.PrintWriter;
@@ -68,6 +73,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.function.Consumer;
 
 /**
@@ -82,7 +88,7 @@
  * see also {@link com.android.systemui.pip.phone.PipMotionHelper}.
  */
 @SysUISingleton
-public class PipTaskOrganizer extends TaskOrganizer implements
+public class PipTaskOrganizer extends TaskOrganizer implements ShellTaskOrganizer.TaskListener,
         DisplayController.OnDisplaysChangedListener {
     private static final String TAG = PipTaskOrganizer.class.getSimpleName();
     private static final boolean DEBUG = false;
@@ -93,6 +99,7 @@
     private static final int MSG_FINISH_RESIZE = 4;
     private static final int MSG_RESIZE_USER = 5;
 
+    private final Context mContext;
     private final Handler mMainHandler;
     private final Handler mUpdateHandler;
     private final PipBoundsHandler mPipBoundsHandler;
@@ -103,7 +110,10 @@
     private final int mEnterExitAnimationDuration;
     private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
     private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
-    private final Divider mSplitDivider;
+    private final Optional<SplitScreen> mSplitScreenOptional;
+    protected final ShellTaskOrganizer mTaskOrganizer;
+    private SurfaceControlViewHost mPipViewHost;
+    private SurfaceControl mPipMenuSurface;
 
     // These callbacks are called on the update thread
     private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
@@ -205,9 +215,11 @@
 
     public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler,
             @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
-            @Nullable Divider divider,
+            Optional<SplitScreen> splitScreenOptional,
             @NonNull DisplayController displayController,
-            @NonNull PipUiEventLogger pipUiEventLogger) {
+            @NonNull PipUiEventLogger pipUiEventLogger,
+            @NonNull ShellTaskOrganizer shellTaskOrganizer) {
+        mContext = context;
         mMainHandler = new Handler(Looper.getMainLooper());
         mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
         mPipBoundsHandler = boundsHandler;
@@ -217,7 +229,9 @@
         mPipAnimationController = new PipAnimationController(mSurfaceTransactionHelper);
         mPipUiEventLoggerLogger = pipUiEventLogger;
         mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
-        mSplitDivider = divider;
+        mSplitScreenOptional = splitScreenOptional;
+        mTaskOrganizer = shellTaskOrganizer;
+        mTaskOrganizer.addListener(this, WINDOWING_MODE_PINNED);
         displayController.addDisplayWindowListener(this);
     }
 
@@ -312,7 +326,7 @@
                     : WINDOWING_MODE_FULLSCREEN);
             wct.setBounds(mToken, destinationBounds);
             wct.setBoundsChangeTransaction(mToken, tx);
-            applySyncTransaction(wct, new WindowContainerTransactionCallback() {
+            mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
                 @Override
                 public void onTransactionReady(int id, SurfaceControl.Transaction t) {
                     t.apply();
@@ -331,9 +345,11 @@
         wct.setWindowingMode(mToken, getOutPipWindowingMode());
         // Simply reset the activity mode set prior to the animation running.
         wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
-        if (mSplitDivider != null && direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
-            wct.reparent(mToken, mSplitDivider.getSecondaryRoot(), true /* onTop */);
-        }
+        mSplitScreenOptional.ifPresent(splitScreen -> {
+            if (direction == TRANSITION_DIRECTION_LEAVE_PIP_TO_SPLIT_SCREEN) {
+                wct.reparent(mToken, splitScreen.getSecondaryRoot(), true /* onTop */);
+            }
+        });
     }
 
     /**
@@ -442,7 +458,7 @@
         wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
         wct.setBounds(mToken, destinationBounds);
         wct.scheduleFinishEnterPip(mToken, destinationBounds);
-        applySyncTransaction(wct, new WindowContainerTransactionCallback() {
+        mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() {
             @Override
             public void onTransactionReady(int id, SurfaceControl.Transaction t) {
                 t.apply();
@@ -496,6 +512,45 @@
     }
 
     /**
+     * Setup the ViewHost and attach the provided menu view to the ViewHost.
+     */
+    public void attachPipMenuViewHost(View menuView, WindowManager.LayoutParams lp) {
+        if (mPipMenuSurface != null) {
+            Log.e(TAG, "PIP Menu View already created and attached.");
+            return;
+        }
+
+        if (Looper.getMainLooper() != Looper.myLooper()) {
+            throw new RuntimeException("PipMenuView needs to be attached on the main thread.");
+        }
+
+        mPipViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(),
+                (android.os.Binder) null);
+        mPipMenuSurface = mPipViewHost.getSurfacePackage().getSurfaceControl();
+        SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+        transaction.reparent(mPipMenuSurface, mLeash);
+        transaction.show(mPipMenuSurface);
+        transaction.setRelativeLayer(mPipMenuSurface, mLeash, 1);
+        transaction.apply();
+        mPipViewHost.setView(menuView, lp);
+    }
+
+
+    /**
+     * Releases the PIP Menu's View host, remove it from PIP task surface.
+     */
+    public void detachPipMenuViewHost() {
+        if (mPipMenuSurface != null) {
+            SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+            transaction.remove(mPipMenuSurface);
+            transaction.apply();
+            mPipMenuSurface = null;
+            mPipViewHost = null;
+        }
+    }
+
+
+    /**
      * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}.
      * Meanwhile this callback is invoked whenever the task is removed. For instance:
      *   - as a result of removeStacksInWindowingModes from WM
@@ -539,11 +594,6 @@
     }
 
     @Override
-    public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
-        // Do nothing
-    }
-
-    @Override
     public void onFixedRotationStarted(int displayId, int newRotation) {
         mShouldDeferEnteringPip = true;
     }
@@ -835,6 +885,12 @@
         WindowContainerTransaction wct = new WindowContainerTransaction();
         prepareFinishResizeTransaction(destinationBounds, direction, tx, wct);
         applyFinishBoundsResize(wct, direction);
+        runOnMainHandler(() -> {
+            if (mPipViewHost != null) {
+                mPipViewHost.relayout(PipMenuActivityController.getPipMenuLayoutParams(
+                        destinationBounds.width(), destinationBounds.height()));
+            }
+        });
     }
 
     private void prepareFinishResizeTransaction(Rect destinationBounds,
@@ -934,20 +990,26 @@
     }
 
     /**
-     * Sync with {@link #mSplitDivider} on destination bounds if PiP is going to split screen.
+     * Sync with {@link SplitScreen} on destination bounds if PiP is going to split screen.
      *
      * @param destinationBoundsOut contain the updated destination bounds if applicable
      * @return {@code true} if destinationBounds is altered for split screen
      */
     private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut) {
-        if (mSplitDivider == null || !mSplitDivider.isDividerVisible()) {
-            // bail early if system is not in split screen mode
+        if (!mSplitScreenOptional.isPresent()) {
             return false;
         }
+
+        SplitScreen splitScreen = mSplitScreenOptional.get();
+        if (!splitScreen.isDividerVisible()) {
+            // fail early if system is not in split screen mode
+            return false;
+        }
+
         // PiP window will go to split-secondary mode instead of fullscreen, populates the
         // split screen bounds here.
-        destinationBoundsOut.set(
-                mSplitDivider.getView().getNonMinimizedSplitScreenSecondaryBounds());
+        destinationBoundsOut.set(splitScreen.getDividerView()
+                .getNonMinimizedSplitScreenSecondaryBounds());
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index ac07641..5ef5b90 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -49,21 +49,22 @@
 import com.android.systemui.pip.PipSurfaceTransactionHelper;
 import com.android.systemui.pip.PipTaskOrganizer;
 import com.android.systemui.pip.PipUiEventLogger;
-import com.android.systemui.pip.phone.dagger.PipMenuActivityClass;
 import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayChangeController;
 import com.android.wm.shell.common.DisplayController;
 
 import java.io.PrintWriter;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
@@ -265,14 +266,14 @@
 
     @Inject
     public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
-            @PipMenuActivityClass Class<?> pipMenuActivityClass,
             ConfigurationController configController,
             DeviceConfigProxy deviceConfig,
             DisplayController displayController,
-            Divider divider,
+            Optional<SplitScreen> splitScreenOptional,
             FloatingContentCoordinator floatingContentCoordinator,
             SysUiState sysUiState,
-            PipUiEventLogger pipUiEventLogger) {
+            PipUiEventLogger pipUiEventLogger,
+            ShellTaskOrganizer shellTaskOrganizer) {
         mContext = context;
         mActivityManager = ActivityManager.getService();
 
@@ -288,12 +289,13 @@
         mPipBoundsHandler = new PipBoundsHandler(mContext);
         mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context, configController);
         mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler,
-                mPipSurfaceTransactionHelper, divider, mDisplayController, pipUiEventLogger);
+                mPipSurfaceTransactionHelper, splitScreenOptional, mDisplayController,
+                pipUiEventLogger, shellTaskOrganizer);
         mPipTaskOrganizer.registerPipTransitionCallback(this);
         mInputConsumerController = InputConsumerController.getPipInputConsumer();
         mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher);
-        mMenuController = new PipMenuActivityController(context, pipMenuActivityClass,
-                mMediaController, mInputConsumerController);
+        mMenuController = new PipMenuActivityController(context,
+                mMediaController, mInputConsumerController, mPipTaskOrganizer);
         mTouchHandler = new PipTouchHandler(context, mActivityManager,
                 mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer,
                 floatingContentCoordinator, deviceConfig, sysUiState, pipUiEventLogger);
@@ -311,7 +313,6 @@
         configController.addCallback(mOverlayChangedListener);
 
         try {
-            mPipTaskOrganizer.registerOrganizer(WINDOWING_MODE_PINNED);
             ActivityManager.StackInfo stackInfo = ActivityTaskManager.getService().getStackInfo(
                     WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
             if (stackInfo != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
deleted file mode 100644
index 1b1b2de..0000000
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ /dev/null
@@ -1,730 +0,0 @@
-/*
- * Copyright (C) 2016 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.pip.phone;
-
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
-import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
-import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
-import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
-
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ACTIONS;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_ALLOW_TIMEOUT;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_CONTROLLER_MESSENGER;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_DISMISS_FRACTION;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_MENU_STATE;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_MENU_WITH_DELAY;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_SHOW_RESIZE_HANDLE;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_STACK_BOUNDS;
-import static com.android.systemui.pip.phone.PipMenuActivityController.EXTRA_WILL_RESIZE_MENU;
-import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
-import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
-import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.PendingIntent.CanceledException;
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.pm.ParceledListSlice;
-import android.graphics.Color;
-import android.graphics.Rect;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Log;
-import android.util.Pair;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager.LayoutParams;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-
-import com.android.systemui.Interpolators;
-import com.android.wm.shell.R;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Translucent activity that gets started on top of a task in PIP to allow the user to control it.
- * TODO(b/150319024): PipMenuActivity will move to a Window
- */
-public class PipMenuActivity extends Activity {
-
-    private static final String TAG = "PipMenuActivity";
-
-    private static final int MESSAGE_INVALID_TYPE = -1;
-
-    public static final int MESSAGE_SHOW_MENU = 1;
-    public static final int MESSAGE_POKE_MENU = 2;
-    public static final int MESSAGE_HIDE_MENU = 3;
-    public static final int MESSAGE_UPDATE_ACTIONS = 4;
-    public static final int MESSAGE_UPDATE_DISMISS_FRACTION = 5;
-    public static final int MESSAGE_ANIMATION_ENDED = 6;
-    public static final int MESSAGE_POINTER_EVENT = 7;
-    public static final int MESSAGE_MENU_EXPANDED = 8;
-    public static final int MESSAGE_FADE_OUT_MENU = 9;
-    public static final int MESSAGE_UPDATE_MENU_LAYOUT = 10;
-
-    private static final int INITIAL_DISMISS_DELAY = 3500;
-    private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
-    private static final long MENU_FADE_DURATION = 125;
-    private static final long MENU_SLOW_FADE_DURATION = 175;
-    private static final long MENU_SHOW_ON_EXPAND_START_DELAY = 30;
-
-    private static final float MENU_BACKGROUND_ALPHA = 0.3f;
-    private static final float DISMISS_BACKGROUND_ALPHA = 0.6f;
-
-    private static final float DISABLED_ACTION_ALPHA = 0.54f;
-
-    private static final boolean ENABLE_RESIZE_HANDLE = false;
-
-    private int mMenuState;
-    private boolean mResize = true;
-    private boolean mAllowMenuTimeout = true;
-    private boolean mAllowTouches = true;
-
-    private final List<RemoteAction> mActions = new ArrayList<>();
-
-    private AccessibilityManager mAccessibilityManager;
-    private Drawable mBackgroundDrawable;
-    private View mMenuContainer;
-    private LinearLayout mActionsGroup;
-    private int mBetweenActionPaddingLand;
-
-    private AnimatorSet mMenuContainerAnimator;
-
-    private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
-            new ValueAnimator.AnimatorUpdateListener() {
-                @Override
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    final float alpha = (float) animation.getAnimatedValue();
-                    mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA*alpha*255));
-                }
-            };
-
-    private Handler mHandler = new Handler(Looper.getMainLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_SHOW_MENU: {
-                    final Bundle data = (Bundle) msg.obj;
-                    showMenu(data.getInt(EXTRA_MENU_STATE),
-                            data.getParcelable(EXTRA_STACK_BOUNDS),
-                            data.getBoolean(EXTRA_ALLOW_TIMEOUT),
-                            data.getBoolean(EXTRA_WILL_RESIZE_MENU),
-                            data.getBoolean(EXTRA_SHOW_MENU_WITH_DELAY),
-                            data.getBoolean(EXTRA_SHOW_RESIZE_HANDLE));
-                    break;
-                }
-                case MESSAGE_POKE_MENU:
-                    cancelDelayedFinish();
-                    break;
-                case MESSAGE_HIDE_MENU:
-                    hideMenu((Runnable) msg.obj);
-                    break;
-                case MESSAGE_UPDATE_ACTIONS: {
-                    final Bundle data = (Bundle) msg.obj;
-                    final ParceledListSlice<RemoteAction> actions = data.getParcelable(
-                            EXTRA_ACTIONS);
-                    setActions(data.getParcelable(EXTRA_STACK_BOUNDS), actions != null
-                            ? actions.getList() : Collections.emptyList());
-                    break;
-                }
-                case MESSAGE_UPDATE_DISMISS_FRACTION: {
-                    final Bundle data = (Bundle) msg.obj;
-                    updateDismissFraction(data.getFloat(EXTRA_DISMISS_FRACTION));
-                    break;
-                }
-                case MESSAGE_ANIMATION_ENDED: {
-                    mAllowTouches = true;
-                    break;
-                }
-                case MESSAGE_POINTER_EVENT: {
-                    final MotionEvent ev = (MotionEvent) msg.obj;
-                    dispatchPointerEvent(ev);
-                    break;
-                }
-                case MESSAGE_MENU_EXPANDED : {
-                    if (mMenuContainerAnimator == null) {
-                        return;
-                    }
-                    mMenuContainerAnimator.setStartDelay(MENU_SHOW_ON_EXPAND_START_DELAY);
-                    mMenuContainerAnimator.start();
-                    break;
-                }
-                case MESSAGE_FADE_OUT_MENU: {
-                    fadeOutMenu();
-                    break;
-                }
-                case MESSAGE_UPDATE_MENU_LAYOUT: {
-                    if (mPipMenuIconsAlgorithm == null) {
-                        return;
-                    }
-                    final Rect bounds = (Rect) msg.obj;
-                    mPipMenuIconsAlgorithm.onBoundsChanged(bounds);
-                    break;
-                }
-            }
-        }
-    };
-    private Messenger mToControllerMessenger;
-    private Messenger mMessenger = new Messenger(mHandler);
-
-    private final Runnable mFinishRunnable = this::hideMenu;
-
-    protected View mViewRoot;
-    protected View mSettingsButton;
-    protected View mDismissButton;
-    protected View mResizeHandle;
-    protected View mTopEndContainer;
-    protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
-
-    @Override
-    protected void onCreate(@Nullable Bundle savedInstanceState) {
-        // Set the flags to allow us to watch for outside touches and also hide the menu and start
-        // manipulating the PIP in the same touch gesture
-        getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
-
-        super.onCreate(savedInstanceState);
-
-        setContentView(R.layout.pip_menu_activity);
-
-        mAccessibilityManager = getSystemService(AccessibilityManager.class);
-        mBackgroundDrawable = new ColorDrawable(Color.BLACK);
-        mBackgroundDrawable.setAlpha(0);
-        mViewRoot = findViewById(R.id.background);
-        mViewRoot.setBackground(mBackgroundDrawable);
-        mMenuContainer = findViewById(R.id.menu_container);
-        mMenuContainer.setAlpha(0);
-        mTopEndContainer = findViewById(R.id.top_end_container);
-        mSettingsButton = findViewById(R.id.settings);
-        mSettingsButton.setAlpha(0);
-        mSettingsButton.setOnClickListener((v) -> {
-            if (v.getAlpha() != 0) {
-                showSettings();
-            }
-        });
-        mDismissButton = findViewById(R.id.dismiss);
-        mDismissButton.setAlpha(0);
-        mDismissButton.setOnClickListener(v -> dismissPip());
-        findViewById(R.id.expand_button).setOnClickListener(v -> {
-            if (mMenuContainer.getAlpha() != 0) {
-                expandPip();
-            }
-        });
-        mResizeHandle = findViewById(R.id.resize_handle);
-        mResizeHandle.setAlpha(0);
-        mActionsGroup = findViewById(R.id.actions_group);
-        mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
-                R.dimen.pip_between_action_padding_land);
-        mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(this.getApplicationContext());
-        mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer,
-                mResizeHandle, mSettingsButton, mDismissButton);
-        updateFromIntent(getIntent());
-        setTitle(R.string.pip_menu_title);
-        setDisablePreviewScreenshots(true);
-
-        // Hide without an animation.
-        getWindow().setExitTransition(null);
-
-        initAccessibility();
-    }
-
-    private void initAccessibility() {
-        getWindow().getDecorView().setAccessibilityDelegate(new View.AccessibilityDelegate() {
-            @Override
-            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
-                super.onInitializeAccessibilityNodeInfo(host, info);
-                String label = getResources().getString(R.string.pip_menu_title);
-                info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, label));
-            }
-
-            @Override
-            public boolean performAccessibilityAction(View host, int action, Bundle args) {
-                if (action == ACTION_CLICK && mMenuState == MENU_STATE_CLOSE) {
-                    Message m = Message.obtain();
-                    m.what = PipMenuActivityController.MESSAGE_SHOW_MENU;
-                    sendMessage(m, "Could not notify controller to show PIP menu");
-                }
-                return super.performAccessibilityAction(host, action, args);
-            }
-        });
-    }
-
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_ESCAPE) {
-            hideMenu();
-            return true;
-        }
-        return super.onKeyUp(keyCode, event);
-    }
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        updateFromIntent(intent);
-    }
-
-    @Override
-    public void onUserInteraction() {
-        if (mAllowMenuTimeout) {
-            repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
-        }
-    }
-
-    @Override
-    protected void onUserLeaveHint() {
-        super.onUserLeaveHint();
-
-        // If another task is starting on top of the menu, then hide and finish it so that it can be
-        // recreated on the top next time it starts
-        hideMenu();
-    }
-
-    @Override
-    public void onTopResumedActivityChanged(boolean isTopResumedActivity) {
-        super.onTopResumedActivityChanged(isTopResumedActivity);
-        if (!isTopResumedActivity && mMenuState != MENU_STATE_NONE) {
-            hideMenu();
-        }
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-
-        // In cases such as device lock, hide and finish it so that it can be recreated on the top
-        // next time it starts, see also {@link #onUserLeaveHint}
-        hideMenu();
-        cancelDelayedFinish();
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-
-        // Fallback, if we are destroyed for any other reason (like when the task is being reset),
-        // also reset the callback.
-        notifyActivityCallback(null);
-    }
-
-    @Override
-    public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
-        if (!isInPictureInPictureMode) {
-            finish();
-        }
-    }
-
-    /**
-     * Dispatch a pointer event from {@link PipTouchHandler}.
-     */
-    private void dispatchPointerEvent(MotionEvent event) {
-        if (event.isTouchEvent()) {
-            dispatchTouchEvent(event);
-        } else {
-            dispatchGenericMotionEvent(event);
-        }
-    }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-        if (!mAllowTouches) {
-            return false;
-        }
-
-        // On the first action outside the window, hide the menu
-        switch (ev.getAction()) {
-            case MotionEvent.ACTION_OUTSIDE:
-                hideMenu();
-                return true;
-        }
-        return super.dispatchTouchEvent(ev);
-    }
-
-    @Override
-    public void finish() {
-        notifyActivityCallback(null);
-        super.finish();
-    }
-
-    @Override
-    public void setTaskDescription(ActivityManager.TaskDescription taskDescription) {
-        // Do nothing
-    }
-
-    private void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout,
-            boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) {
-        mAllowMenuTimeout = allowMenuTimeout;
-        if (mMenuState != menuState) {
-            // Disallow touches if the menu needs to resize while showing, and we are transitioning
-            // to/from a full menu state.
-            boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow &&
-                    (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
-            mAllowTouches = !disallowTouchesUntilAnimationEnd;
-            cancelDelayedFinish();
-            updateActionViews(stackBounds);
-            if (mMenuContainerAnimator != null) {
-                mMenuContainerAnimator.cancel();
-            }
-            mMenuContainerAnimator = new AnimatorSet();
-            ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
-                    mMenuContainer.getAlpha(), 1f);
-            menuAnim.addUpdateListener(mMenuBgUpdateListener);
-            ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
-                    mSettingsButton.getAlpha(), 1f);
-            ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
-                    mDismissButton.getAlpha(), 1f);
-            ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
-                    mResizeHandle.getAlpha(),
-                    ENABLE_RESIZE_HANDLE && menuState == MENU_STATE_CLOSE && showResizeHandle
-                            ? 1f : 0f);
-            if (menuState == MENU_STATE_FULL) {
-                mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
-                        resizeAnim);
-            } else {
-                mMenuContainerAnimator.playTogether(dismissAnim, resizeAnim);
-            }
-            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
-            mMenuContainerAnimator.setDuration(menuState == MENU_STATE_CLOSE
-                    ? MENU_FADE_DURATION
-                    : MENU_SLOW_FADE_DURATION);
-            if (allowMenuTimeout) {
-                mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        repostDelayedFinish(INITIAL_DISMISS_DELAY);
-                    }
-                });
-            }
-            if (withDelay) {
-                // starts the menu container animation after window expansion is completed
-                notifyMenuStateChange(menuState, resizeMenuOnShow, MESSAGE_MENU_EXPANDED);
-            } else {
-                notifyMenuStateChange(menuState, resizeMenuOnShow, MESSAGE_INVALID_TYPE);
-                mMenuContainerAnimator.start();
-            }
-        } else {
-            // If we are already visible, then just start the delayed dismiss and unregister any
-            // existing input consumers from the previous drag
-            if (allowMenuTimeout) {
-                repostDelayedFinish(POST_INTERACTION_DISMISS_DELAY);
-            }
-        }
-    }
-
-    /**
-     * Different from {@link #hideMenu()}, this function does not try to finish this menu activity
-     * and instead, it fades out the controls by setting the alpha to 0 directly without menu
-     * visibility callbacks invoked.
-     */
-    private void fadeOutMenu() {
-        mMenuContainer.setAlpha(0f);
-        mSettingsButton.setAlpha(0f);
-        mDismissButton.setAlpha(0f);
-        mResizeHandle.setAlpha(0f);
-    }
-
-    private void hideMenu() {
-        hideMenu(null);
-    }
-
-    private void hideMenu(Runnable animationEndCallback) {
-        hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false /* isDismissing */,
-                true /* animate */);
-    }
-
-    private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
-            boolean isDismissing, boolean animate) {
-        if (mMenuState != MENU_STATE_NONE) {
-            cancelDelayedFinish();
-            if (notifyMenuVisibility) {
-                notifyMenuStateChange(MENU_STATE_NONE, mResize, MESSAGE_INVALID_TYPE);
-            }
-            mMenuContainerAnimator = new AnimatorSet();
-            ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
-                    mMenuContainer.getAlpha(), 0f);
-            menuAnim.addUpdateListener(mMenuBgUpdateListener);
-            ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
-                    mSettingsButton.getAlpha(), 0f);
-            ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
-                    mDismissButton.getAlpha(), 0f);
-            ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
-                    mResizeHandle.getAlpha(), 0f);
-            mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, resizeAnim);
-            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
-            mMenuContainerAnimator.setDuration(animate ? MENU_FADE_DURATION : 0);
-            mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (animationFinishedRunnable != null) {
-                        animationFinishedRunnable.run();
-                    }
-
-                    if (!isDismissing) {
-                        // If we are dismissing the PiP, then don't try to pre-emptively finish the
-                        // menu activity
-                        finish();
-                    }
-                }
-            });
-            mMenuContainerAnimator.start();
-        } else {
-            // If the menu is not visible, just finish now
-            finish();
-        }
-    }
-
-    private void updateFromIntent(Intent intent) {
-        mToControllerMessenger = intent.getParcelableExtra(EXTRA_CONTROLLER_MESSENGER);
-        if (mToControllerMessenger == null) {
-            Log.w(TAG, "Controller messenger is null. Stopping.");
-            finish();
-            return;
-        }
-        notifyActivityCallback(mMessenger);
-
-        ParceledListSlice<RemoteAction> actions = intent.getParcelableExtra(EXTRA_ACTIONS);
-        if (actions != null) {
-            mActions.clear();
-            mActions.addAll(actions.getList());
-        }
-
-        final int menuState = intent.getIntExtra(EXTRA_MENU_STATE, MENU_STATE_NONE);
-        if (menuState != MENU_STATE_NONE) {
-            Rect stackBounds = intent.getParcelableExtra(EXTRA_STACK_BOUNDS);
-            boolean allowMenuTimeout = intent.getBooleanExtra(EXTRA_ALLOW_TIMEOUT, true);
-            boolean willResizeMenu = intent.getBooleanExtra(EXTRA_WILL_RESIZE_MENU, false);
-            boolean withDelay = intent.getBooleanExtra(EXTRA_SHOW_MENU_WITH_DELAY, false);
-            boolean showResizeHandle = intent.getBooleanExtra(EXTRA_SHOW_RESIZE_HANDLE, false);
-            showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay,
-                    showResizeHandle);
-        }
-    }
-
-    private void setActions(Rect stackBounds, List<RemoteAction> actions) {
-        mActions.clear();
-        mActions.addAll(actions);
-        updateActionViews(stackBounds);
-    }
-
-    private void updateActionViews(Rect stackBounds) {
-        ViewGroup expandContainer = findViewById(R.id.expand_container);
-        ViewGroup actionsContainer = findViewById(R.id.actions_container);
-        actionsContainer.setOnTouchListener((v, ev) -> {
-            // Do nothing, prevent click through to parent
-            return true;
-        });
-
-        if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
-            actionsContainer.setVisibility(View.INVISIBLE);
-        } else {
-            actionsContainer.setVisibility(View.VISIBLE);
-            if (mActionsGroup != null) {
-                // Ensure we have as many buttons as actions
-                final LayoutInflater inflater = LayoutInflater.from(this);
-                while (mActionsGroup.getChildCount() < mActions.size()) {
-                    final ImageButton actionView = (ImageButton) inflater.inflate(
-                            R.layout.pip_menu_action, mActionsGroup, false);
-                    mActionsGroup.addView(actionView);
-                }
-
-                // Update the visibility of all views
-                for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
-                    mActionsGroup.getChildAt(i).setVisibility(i < mActions.size()
-                            ? View.VISIBLE
-                            : View.GONE);
-                }
-
-                // Recreate the layout
-                final boolean isLandscapePip = stackBounds != null &&
-                        (stackBounds.width() > stackBounds.height());
-                for (int i = 0; i < mActions.size(); i++) {
-                    final RemoteAction action = mActions.get(i);
-                    final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i);
-
-                    // TODO: Check if the action drawable has changed before we reload it
-                    action.getIcon().loadDrawableAsync(this, d -> {
-                        d.setTint(Color.WHITE);
-                        actionView.setImageDrawable(d);
-                    }, mHandler);
-                    actionView.setContentDescription(action.getContentDescription());
-                    if (action.isEnabled()) {
-                        actionView.setOnClickListener(v -> {
-                            mHandler.post(() -> {
-                                try {
-                                    action.getActionIntent().send();
-                                } catch (CanceledException e) {
-                                    Log.w(TAG, "Failed to send action", e);
-                                }
-                            });
-                        });
-                    }
-                    actionView.setEnabled(action.isEnabled());
-                    actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
-
-                    // Update the margin between actions
-                    LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
-                            actionView.getLayoutParams();
-                    lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0;
-                }
-            }
-
-            // Update the expand container margin to adjust the center of the expand button to
-            // account for the existence of the action container
-            FrameLayout.LayoutParams expandedLp =
-                    (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
-            expandedLp.topMargin = getResources().getDimensionPixelSize(
-                    R.dimen.pip_action_padding);
-            expandedLp.bottomMargin = getResources().getDimensionPixelSize(
-                    R.dimen.pip_expand_container_edge_margin);
-            expandContainer.requestLayout();
-        }
-    }
-
-    private void updateDismissFraction(float fraction) {
-        int alpha;
-        final float menuAlpha = 1 - fraction;
-        if (mMenuState == MENU_STATE_FULL) {
-            mMenuContainer.setAlpha(menuAlpha);
-            mSettingsButton.setAlpha(menuAlpha);
-            mDismissButton.setAlpha(menuAlpha);
-            final float interpolatedAlpha =
-                    MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction;
-            alpha = (int) (interpolatedAlpha * 255);
-        } else {
-            if (mMenuState == MENU_STATE_CLOSE) {
-                mDismissButton.setAlpha(menuAlpha);
-            }
-            alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
-        }
-        mBackgroundDrawable.setAlpha(alpha);
-    }
-
-    private void notifyMenuStateChange(int menuState, boolean resize, int callbackWhat) {
-        mMenuState = menuState;
-        mResize = resize;
-        Message m = Message.obtain();
-        m.what = PipMenuActivityController.MESSAGE_MENU_STATE_CHANGED;
-        m.arg1 = menuState;
-        m.arg2 = resize ? 1 : 0;
-        if (callbackWhat != MESSAGE_INVALID_TYPE) {
-            // This message could be sent across processes when in secondary user.
-            // Make the receiver end sending back via our own Messenger
-            m.replyTo = mMessenger;
-            final Bundle data = new Bundle(1);
-            data.putInt(PipMenuActivityController.EXTRA_MESSAGE_CALLBACK_WHAT, callbackWhat);
-            m.obj = data;
-        }
-        sendMessage(m, "Could not notify controller of PIP menu visibility");
-    }
-
-    private void expandPip() {
-        // Do not notify menu visibility when hiding the menu, the controller will do this when it
-        // handles the message
-        hideMenu(() -> {
-            sendEmptyMessage(PipMenuActivityController.MESSAGE_EXPAND_PIP,
-                    "Could not notify controller to expand PIP");
-        }, false /* notifyMenuVisibility */, false /* isDismissing */, true /* animate */);
-    }
-
-    private void dismissPip() {
-        // Since tapping on the close-button invokes a double-tap wait callback in PipTouchHandler,
-        // we want to disable animating the fadeout animation of the buttons in order to call on
-        // PipTouchHandler#onPipDismiss fast enough.
-        final boolean animate = mMenuState != MENU_STATE_CLOSE;
-        // Do not notify menu visibility when hiding the menu, the controller will do this when it
-        // handles the message
-        hideMenu(() -> {
-            sendEmptyMessage(PipMenuActivityController.MESSAGE_DISMISS_PIP,
-                    "Could not notify controller to dismiss PIP");
-        }, false /* notifyMenuVisibility */, true /* isDismissing */, animate);
-    }
-
-    private void showSettings() {
-        final Pair<ComponentName, Integer> topPipActivityInfo =
-                PipUtils.getTopPipActivity(this, ActivityManager.getService());
-        if (topPipActivityInfo.first != null) {
-            final UserHandle user = UserHandle.of(topPipActivityInfo.second);
-            final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
-                    Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
-            settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
-            settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
-            startActivity(settingsIntent);
-        }
-    }
-
-    private void notifyActivityCallback(Messenger callback) {
-        Message m = Message.obtain();
-        m.what = PipMenuActivityController.MESSAGE_UPDATE_ACTIVITY_CALLBACK;
-        m.replyTo = callback;
-        m.arg1 = mResize ?  1 : 0;
-        sendMessage(m, "Could not notify controller of activity finished");
-    }
-
-    private void sendEmptyMessage(int what, String errorMsg) {
-        Message m = Message.obtain();
-        m.what = what;
-        sendMessage(m, errorMsg);
-    }
-
-    private void sendMessage(Message m, String errorMsg) {
-        if (mToControllerMessenger == null) {
-            return;
-        }
-        try {
-            mToControllerMessenger.send(m);
-        } catch (RemoteException e) {
-            Log.e(TAG, errorMsg, e);
-        }
-    }
-
-    private void cancelDelayedFinish() {
-        mHandler.removeCallbacks(mFinishRunnable);
-    }
-
-    private void repostDelayedFinish(int delay) {
-        int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
-                FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
-        mHandler.removeCallbacks(mFinishRunnable);
-        mHandler.postDelayed(mFinishRunnable, recommendedTimeout);
-    }
-}
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 383f6b3..873ba26 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java
@@ -19,27 +19,20 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
-import android.annotation.Nullable;
 import android.app.ActivityManager.StackInfo;
-import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.RemoteAction;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.ParceledListSlice;
+import android.graphics.PixelFormat;
 import android.graphics.Rect;
-import android.os.Bundle;
 import android.os.Debug;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
 import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
 import android.util.Log;
 import android.view.MotionEvent;
+import android.view.WindowManager;
 
+import com.android.systemui.pip.PipTaskOrganizer;
 import com.android.systemui.pip.phone.PipMediaController.ActionListener;
 import com.android.systemui.shared.system.InputConsumerController;
 
@@ -58,30 +51,10 @@
     private static final String TAG = "PipMenuActController";
     private static final boolean DEBUG = false;
 
-    public static final String EXTRA_CONTROLLER_MESSENGER = "messenger";
-    public static final String EXTRA_ACTIONS = "actions";
-    public static final String EXTRA_STACK_BOUNDS = "stack_bounds";
-    public static final String EXTRA_ALLOW_TIMEOUT = "allow_timeout";
-    public static final String EXTRA_WILL_RESIZE_MENU = "resize_menu_on_show";
-    public static final String EXTRA_DISMISS_FRACTION = "dismiss_fraction";
-    public static final String EXTRA_MENU_STATE = "menu_state";
-    public static final String EXTRA_SHOW_MENU_WITH_DELAY = "show_menu_with_delay";
-    public static final String EXTRA_SHOW_RESIZE_HANDLE = "show_resize_handle";
-    public static final String EXTRA_MESSAGE_CALLBACK_WHAT = "message_callback_what";
-
-    public static final int MESSAGE_MENU_STATE_CHANGED = 100;
-    public static final int MESSAGE_EXPAND_PIP = 101;
-    public static final int MESSAGE_DISMISS_PIP = 103;
-    public static final int MESSAGE_UPDATE_ACTIVITY_CALLBACK = 104;
-    public static final int MESSAGE_SHOW_MENU = 107;
-
     public static final int MENU_STATE_NONE = 0;
     public static final int MENU_STATE_CLOSE = 1;
     public static final int MENU_STATE_FULL = 2;
 
-    // The duration to wait before we consider the start activity as having timed out
-    private static final long START_ACTIVITY_REQUEST_TIMEOUT_MS = 300;
-
     /**
      * A listener interface to receive notification on changes in PIP.
      */
@@ -110,9 +83,8 @@
         void onPipShowMenu();
     }
 
-    /** TODO(b/150319024): PipMenuActivity will move to a Window */
-    private Class<?> mPipMenuActivityClass;
     private Context mContext;
+    private PipTaskOrganizer mPipTaskOrganizer;
     private PipMediaController mMediaController;
     private InputConsumerController mInputConsumerController;
 
@@ -121,63 +93,7 @@
     private ParceledListSlice<RemoteAction> mMediaActions;
     private int mMenuState;
 
-    // The dismiss fraction update is sent frequently, so use a temporary bundle for the message
-    private Bundle mTmpDismissFractionData = new Bundle();
-
-    private Runnable mOnAnimationEndRunnable;
-    private boolean mStartActivityRequested;
-    private long mStartActivityRequestedTime;
-    private Messenger mToActivityMessenger;
-    private Handler mHandler = new Handler(Looper.getMainLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MESSAGE_MENU_STATE_CHANGED: {
-                    final int menuState = msg.arg1;
-                    final boolean resize = msg.arg2 != 0;
-                    onMenuStateChanged(menuState, resize,
-                            getMenuStateChangeFinishedCallback(msg.replyTo, (Bundle) msg.obj));
-                    break;
-                }
-                case MESSAGE_EXPAND_PIP: {
-                    mListeners.forEach(Listener::onPipExpand);
-                    break;
-                }
-                case MESSAGE_DISMISS_PIP: {
-                    mListeners.forEach(Listener::onPipDismiss);
-                    break;
-                }
-                case MESSAGE_SHOW_MENU: {
-                    mListeners.forEach(Listener::onPipShowMenu);
-                    break;
-                }
-                case MESSAGE_UPDATE_ACTIVITY_CALLBACK: {
-                    mToActivityMessenger = msg.replyTo;
-                    setStartActivityRequested(false);
-                    if (mOnAnimationEndRunnable != null) {
-                        mOnAnimationEndRunnable.run();
-                        mOnAnimationEndRunnable = null;
-                    }
-                    // Mark the menu as invisible once the activity finishes as well
-                    if (mToActivityMessenger == null) {
-                        final boolean resize = msg.arg1 != 0;
-                        onMenuStateChanged(MENU_STATE_NONE, resize, null /* callback */);
-                    }
-                    break;
-                }
-            }
-        }
-    };
-    private Messenger mMessenger = new Messenger(mHandler);
-
-    private Runnable mStartActivityRequestedTimeoutRunnable = () -> {
-        setStartActivityRequested(false);
-        if (mOnAnimationEndRunnable != null) {
-            mOnAnimationEndRunnable.run();
-            mOnAnimationEndRunnable = null;
-        }
-        Log.e(TAG, "Expected start menu activity request timed out");
-    };
+    private PipMenuView mPipMenuView;
 
     private ActionListener mMediaActionListener = new ActionListener() {
         @Override
@@ -187,39 +103,40 @@
         }
     };
 
-    public PipMenuActivityController(Context context, Class<?> pipMenuActivityClass,
-            PipMediaController mediaController, InputConsumerController inputConsumerController
+    public PipMenuActivityController(Context context,
+            PipMediaController mediaController, InputConsumerController inputConsumerController,
+            PipTaskOrganizer pipTaskOrganizer
     ) {
         mContext = context;
         mMediaController = mediaController;
         mInputConsumerController = inputConsumerController;
-        mPipMenuActivityClass = pipMenuActivityClass;
+        mPipTaskOrganizer = pipTaskOrganizer;
     }
 
-    public boolean isMenuActivityVisible() {
-        return mToActivityMessenger != null;
+    public boolean isMenuVisible() {
+        return mPipMenuView != null && mMenuState != MENU_STATE_NONE;
     }
 
     public void onActivityPinned() {
+        if (mPipMenuView == null) {
+            WindowManager.LayoutParams lp =
+                    getPipMenuLayoutParams(0, 0);
+            mPipMenuView = new PipMenuView(mContext, this);
+            mPipTaskOrganizer.attachPipMenuViewHost(mPipMenuView, lp);
+        }
         mInputConsumerController.registerInputConsumer(true /* withSfVsync */);
     }
 
     public void onActivityUnpinned() {
         hideMenu();
         mInputConsumerController.unregisterInputConsumer();
-        setStartActivityRequested(false);
+        mPipTaskOrganizer.detachPipMenuViewHost();
+        mPipMenuView = null;
     }
 
     public void onPinnedStackAnimationEnded() {
-        // Note: Only active menu activities care about this event
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_ANIMATION_ENDED;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu pinned animation ended", e);
-            }
+        if (isMenuVisible()) {
+            mPipMenuView.onPipAnimationEnded();
         }
     }
 
@@ -236,27 +153,13 @@
      * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
      */
     public void setDismissFraction(float fraction) {
+        final boolean isMenuVisible = isMenuVisible();
         if (DEBUG) {
-            Log.d(TAG, "setDismissFraction() hasActivity=" + (mToActivityMessenger != null)
+            Log.d(TAG, "setDismissFraction() isMenuVisible=" + isMenuVisible
                     + " fraction=" + fraction);
         }
-        if (mToActivityMessenger != null) {
-            mTmpDismissFractionData.clear();
-            mTmpDismissFractionData.putFloat(EXTRA_DISMISS_FRACTION, fraction);
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_UPDATE_DISMISS_FRACTION;
-            m.obj = mTmpDismissFractionData;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu to update dismiss fraction", e);
-            }
-        } else if (!mStartActivityRequested || isStartActivityRequestedElapsed()) {
-            // If we haven't requested the start activity, or if it previously took too long to
-            // start, then start it
-            startMenuActivity(MENU_STATE_NONE, null /* stackBounds */,
-                    false /* allowMenuTimeout */, false /* resizeMenuOnShow */,
-                    false /* withDelay */, false /* showResizeHandle */);
+        if (isMenuVisible) {
+            mPipMenuView.updateDismissFraction(fraction);
         }
     }
 
@@ -282,27 +185,11 @@
                 false /* withDelay */, showResizeHandle);
     }
 
-    private Runnable getMenuStateChangeFinishedCallback(@Nullable Messenger replyTo,
-            @Nullable Bundle data) {
-        if (replyTo == null || data == null) {
-            return null;
-        }
-        return () -> {
-            try {
-                final Message m = Message.obtain();
-                m.what = data.getInt(EXTRA_MESSAGE_CALLBACK_WHAT);
-                replyTo.send(m);
-            } catch (RemoteException e) {
-                // ignored
-            }
-        };
-    }
-
     private void showMenuInternal(int menuState, Rect stackBounds, boolean allowMenuTimeout,
             boolean willResizeMenu, boolean withDelay, boolean showResizeHandle) {
         if (DEBUG) {
             Log.d(TAG, "showMenu() state=" + menuState
-                    + " hasActivity=" + (mToActivityMessenger != null)
+                    + " isMenuVisible=" + isMenuVisible()
                     + " allowMenuTimeout=" + allowMenuTimeout
                     + " willResizeMenu=" + willResizeMenu
                     + " withDelay=" + withDelay
@@ -310,64 +197,34 @@
                     + " callers=\n" + Debug.getCallers(5, "    "));
         }
 
-        if (mToActivityMessenger != null) {
-            Bundle data = new Bundle();
-            data.putInt(EXTRA_MENU_STATE, menuState);
-            if (stackBounds != null) {
-                data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds);
-            }
-            data.putBoolean(EXTRA_ALLOW_TIMEOUT, allowMenuTimeout);
-            data.putBoolean(EXTRA_WILL_RESIZE_MENU, willResizeMenu);
-            data.putBoolean(EXTRA_SHOW_MENU_WITH_DELAY, withDelay);
-            data.putBoolean(EXTRA_SHOW_RESIZE_HANDLE, showResizeHandle);
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_SHOW_MENU;
-            m.obj = data;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu to show", e);
-            }
-        } else if (!mStartActivityRequested || isStartActivityRequestedElapsed()) {
-            // If we haven't requested the start activity, or if it previously took too long to
-            // start, then start it
-            startMenuActivity(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay,
-                    showResizeHandle);
+        if (mPipMenuView == null) {
+            Log.e(TAG, "PipMenu has not been attached yet.");
+            return;
         }
+        mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay,
+                showResizeHandle);
     }
 
     /**
      * Pokes the menu, indicating that the user is interacting with it.
      */
     public void pokeMenu() {
+        final boolean isMenuVisible = isMenuVisible();
         if (DEBUG) {
-            Log.d(TAG, "pokeMenu() hasActivity=" + (mToActivityMessenger != null));
+            Log.d(TAG, "pokeMenu() isMenuVisible=" + isMenuVisible);
         }
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_POKE_MENU;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify poke menu", e);
-            }
+        if (isMenuVisible) {
+            mPipMenuView.pokeMenu();
         }
     }
 
     private void fadeOutMenu() {
+        final boolean isMenuVisible = isMenuVisible();
         if (DEBUG) {
-            Log.d(TAG, "fadeOutMenu() state=" + mMenuState
-                    + " hasActivity=" + (mToActivityMessenger != null)
-                    + " callers=\n" + Debug.getCallers(5, "    "));
+            Log.d(TAG, "fadeOutMenu() isMenuVisible=" + isMenuVisible);
         }
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_FADE_OUT_MENU;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu to fade out", e);
-            }
+        if (isMenuVisible) {
+            mPipMenuView.fadeOutMenu();
         }
     }
 
@@ -375,19 +232,14 @@
      * Hides the menu activity.
      */
     public void hideMenu() {
+        final boolean isMenuVisible = isMenuVisible();
         if (DEBUG) {
             Log.d(TAG, "hideMenu() state=" + mMenuState
-                    + " hasActivity=" + (mToActivityMessenger != null)
+                    + " isMenuVisible=" + isMenuVisible
                     + " callers=\n" + Debug.getCallers(5, "    "));
         }
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_HIDE_MENU;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu to hide", e);
-            }
+        if (isMenuVisible) {
+            mPipMenuView.hideMenu();
         }
     }
 
@@ -395,29 +247,11 @@
      * Hides the menu activity.
      */
     public void hideMenu(Runnable onStartCallback, Runnable onEndCallback) {
-        if (mStartActivityRequested) {
-            // If the menu has been start-requested, but not actually started, then we defer the
-            // trigger callback until the menu has started and called back to the controller.
-            mOnAnimationEndRunnable = onEndCallback;
-            onStartCallback.run();
-
-            // Fallback for b/63752800, we have started the PipMenuActivity but it has not made any
-            // callbacks. Don't continue to wait for the menu to show past some timeout.
-            mHandler.removeCallbacks(mStartActivityRequestedTimeoutRunnable);
-            mHandler.postDelayed(mStartActivityRequestedTimeoutRunnable,
-                    START_ACTIVITY_REQUEST_TIMEOUT_MS);
-        } else if (mMenuState != MENU_STATE_NONE && mToActivityMessenger != null) {
+        if (isMenuVisible()) {
             // If the menu is visible in either the closed or full state, then hide the menu and
             // trigger the animation trigger afterwards
             onStartCallback.run();
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_HIDE_MENU;
-            m.obj = onEndCallback;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify hide menu", e);
-            }
+            mPipMenuView.hideMenu(onEndCallback);
         }
     }
 
@@ -438,6 +272,18 @@
         updateMenuActions();
     }
 
+    void onPipExpand() {
+        mListeners.forEach(Listener::onPipExpand);
+    }
+
+    void onPipDismiss() {
+        mListeners.forEach(Listener::onPipDismiss);
+    }
+
+    void onPipShowMenu() {
+        mListeners.forEach(Listener::onPipShowMenu);
+    }
+
     /**
      * @return the best set of actions to show in the PiP menu.
      */
@@ -449,47 +295,20 @@
     }
 
     /**
-     * Starts the menu activity on the top task of the pinned stack.
+     * Returns a default LayoutParams for the PIP Menu.
+     * @param width the PIP stack width.
+     * @param height the PIP stack height.
      */
-    private void startMenuActivity(int menuState, Rect stackBounds, boolean allowMenuTimeout,
-            boolean willResizeMenu, boolean withDelay, boolean showResizeHandle) {
-        try {
-            StackInfo pinnedStackInfo = ActivityTaskManager.getService().getStackInfo(
-                    WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
-            if (pinnedStackInfo != null && pinnedStackInfo.taskIds != null &&
-                    pinnedStackInfo.taskIds.length > 0) {
-                Intent intent = new Intent(mContext, mPipMenuActivityClass);
-                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                intent.putExtra(EXTRA_CONTROLLER_MESSENGER, mMessenger);
-                intent.putExtra(EXTRA_ACTIONS, resolveMenuActions());
-                if (stackBounds != null) {
-                    intent.putExtra(EXTRA_STACK_BOUNDS, stackBounds);
-                }
-                intent.putExtra(EXTRA_MENU_STATE, menuState);
-                intent.putExtra(EXTRA_ALLOW_TIMEOUT, allowMenuTimeout);
-                intent.putExtra(EXTRA_WILL_RESIZE_MENU, willResizeMenu);
-                intent.putExtra(EXTRA_SHOW_MENU_WITH_DELAY, withDelay);
-                intent.putExtra(EXTRA_SHOW_RESIZE_HANDLE, showResizeHandle);
-                ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
-                options.setLaunchTaskId(
-                        pinnedStackInfo.taskIds[pinnedStackInfo.taskIds.length - 1]);
-                options.setTaskOverlay(true, true /* canResume */);
-                mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT);
-                setStartActivityRequested(true);
-            } else {
-                Log.e(TAG, "No PIP tasks found");
-            }
-        } catch (RemoteException e) {
-            setStartActivityRequested(false);
-            Log.e(TAG, "Error showing PIP menu activity", e);
-        }
+    public static WindowManager.LayoutParams getPipMenuLayoutParams(int width, int height) {
+        return new WindowManager.LayoutParams(width, height,
+                WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSLUCENT);
     }
 
     /**
-     * Updates the PiP menu activity with the best set of actions provided.
+     * Updates the PiP menu with the best set of actions provided.
      */
     private void updateMenuActions() {
-        if (mToActivityMessenger != null) {
+        if (isMenuVisible()) {
             // Fetch the pinned stack bounds
             Rect stackBounds = null;
             try {
@@ -499,20 +318,10 @@
                     stackBounds = pinnedStackInfo.bounds;
                 }
             } catch (RemoteException e) {
-                Log.e(TAG, "Error showing PIP menu activity", e);
+                Log.e(TAG, "Error showing PIP menu", e);
             }
 
-            Bundle data = new Bundle();
-            data.putParcelable(EXTRA_STACK_BOUNDS, stackBounds);
-            data.putParcelable(EXTRA_ACTIONS, resolveMenuActions());
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_UPDATE_ACTIONS;
-            m.obj = data;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not notify menu activity to update actions", e);
-            }
+            mPipMenuView.setActions(stackBounds, resolveMenuActions().getList());
         }
     }
 
@@ -524,17 +333,9 @@
     }
 
     /**
-     * @return whether the time of the activity request has exceeded the timeout.
-     */
-    private boolean isStartActivityRequestedElapsed() {
-        return (SystemClock.uptimeMillis() - mStartActivityRequestedTime)
-                >= START_ACTIVITY_REQUEST_TIMEOUT_MS;
-    }
-
-    /**
      * Handles changes in menu visibility.
      */
-    private void onMenuStateChanged(int menuState, boolean resize, Runnable callback) {
+    void onMenuStateChanged(int menuState, boolean resize, Runnable callback) {
         if (DEBUG) {
             Log.d(TAG, "onMenuStateChanged() mMenuState=" + mMenuState
                     + " menuState=" + menuState + " resize=" + resize
@@ -556,25 +357,14 @@
         mMenuState = menuState;
     }
 
-    private void setStartActivityRequested(boolean requested) {
-        mHandler.removeCallbacks(mStartActivityRequestedTimeoutRunnable);
-        mStartActivityRequested = requested;
-        mStartActivityRequestedTime = requested ? SystemClock.uptimeMillis() : 0;
-    }
-
     /**
      * Handles a pointer event sent from pip input consumer.
      */
     void handlePointerEvent(MotionEvent ev) {
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_POINTER_EVENT;
-            m.obj = ev;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not dispatch touch event", e);
-            }
+        if (ev.isTouchEvent()) {
+            mPipMenuView.dispatchTouchEvent(ev);
+        } else {
+            mPipMenuView.dispatchGenericMotionEvent(ev);
         }
     }
 
@@ -582,15 +372,14 @@
      * Tell the PIP Menu to recalculate its layout given its current position on the display.
      */
     public void updateMenuLayout(Rect bounds) {
-        if (mToActivityMessenger != null) {
-            Message m = Message.obtain();
-            m.what = PipMenuActivity.MESSAGE_UPDATE_MENU_LAYOUT;
-            m.obj = bounds;
-            try {
-                mToActivityMessenger.send(m);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Could not dispatch touch event", e);
-            }
+        final boolean isMenuVisible = isMenuVisible();
+        if (DEBUG) {
+            Log.d(TAG, "updateMenuLayout() state=" + mMenuState
+                    + " isMenuVisible=" + isMenuVisible
+                    + " callers=\n" + Debug.getCallers(5, "    "));
+        }
+        if (isMenuVisible) {
+            mPipMenuView.updateMenuLayout(bounds);
         }
     }
 
@@ -598,9 +387,7 @@
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
         pw.println(innerPrefix + "mMenuState=" + mMenuState);
-        pw.println(innerPrefix + "mToActivityMessenger=" + mToActivityMessenger);
+        pw.println(innerPrefix + "mPipMenuView=" + mPipMenuView);
         pw.println(innerPrefix + "mListeners=" + mListeners.size());
-        pw.println(innerPrefix + "mStartActivityRequested=" + mStartActivityRequested);
-        pw.println(innerPrefix + "mStartActivityRequestedTime=" + mStartActivityRequestedTime);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
new file mode 100644
index 0000000..993bfe0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuView.java
@@ -0,0 +1,497 @@
+/*
+ * 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.pip.phone;
+
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.provider.Settings.ACTION_PICTURE_IN_PICTURE_SETTINGS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
+import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
+
+import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_CLOSE;
+import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_FULL;
+import static com.android.systemui.pip.phone.PipMenuActivityController.MENU_STATE_NONE;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.ActivityManager;
+import android.app.PendingIntent.CanceledException;
+import android.app.RemoteAction;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.Log;
+import android.util.Pair;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Translucent window that gets started on top of a task in PIP to allow the user to control it.
+ */
+public class PipMenuView extends FrameLayout {
+
+    private static final String TAG = "PipMenuView";
+
+    private static final int MESSAGE_INVALID_TYPE = -1;
+    public static final int MESSAGE_MENU_EXPANDED = 8;
+
+    private static final int INITIAL_DISMISS_DELAY = 3500;
+    private static final int POST_INTERACTION_DISMISS_DELAY = 2000;
+    private static final long MENU_FADE_DURATION = 125;
+    private static final long MENU_SLOW_FADE_DURATION = 175;
+    private static final long MENU_SHOW_ON_EXPAND_START_DELAY = 30;
+
+    private static final float MENU_BACKGROUND_ALPHA = 0.3f;
+    private static final float DISMISS_BACKGROUND_ALPHA = 0.6f;
+
+    private static final float DISABLED_ACTION_ALPHA = 0.54f;
+
+    private static final boolean ENABLE_RESIZE_HANDLE = false;
+
+    private int mMenuState;
+    private boolean mResize = true;
+    private boolean mAllowMenuTimeout = true;
+    private boolean mAllowTouches = true;
+
+    private final List<RemoteAction> mActions = new ArrayList<>();
+
+    private AccessibilityManager mAccessibilityManager;
+    private Drawable mBackgroundDrawable;
+    private View mMenuContainer;
+    private LinearLayout mActionsGroup;
+    private int mBetweenActionPaddingLand;
+
+    private AnimatorSet mMenuContainerAnimator;
+    private PipMenuActivityController mController;
+
+    private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener =
+            new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    final float alpha = (float) animation.getAnimatedValue();
+                    mBackgroundDrawable.setAlpha((int) (MENU_BACKGROUND_ALPHA * alpha * 255));
+                }
+            };
+
+    private Handler mHandler = new Handler();
+
+    private final Runnable mHideMenuRunnable = this::hideMenu;
+
+    protected View mViewRoot;
+    protected View mSettingsButton;
+    protected View mDismissButton;
+    protected View mResizeHandle;
+    protected View mTopEndContainer;
+    protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm;
+
+    public PipMenuView(Context context, PipMenuActivityController controller) {
+        super(context, null, 0);
+        mContext = context;
+        mController = controller;
+
+        mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+        inflate(context, R.layout.pip_menu, this);
+
+        mBackgroundDrawable = new ColorDrawable(Color.BLACK);
+        mBackgroundDrawable.setAlpha(0);
+        mViewRoot = findViewById(R.id.background);
+        mViewRoot.setBackground(mBackgroundDrawable);
+        mMenuContainer = findViewById(R.id.menu_container);
+        mMenuContainer.setAlpha(0);
+        mTopEndContainer = findViewById(R.id.top_end_container);
+        mSettingsButton = findViewById(R.id.settings);
+        mSettingsButton.setAlpha(0);
+        mSettingsButton.setOnClickListener((v) -> {
+            if (v.getAlpha() != 0) {
+                showSettings();
+            }
+        });
+        mDismissButton = findViewById(R.id.dismiss);
+        mDismissButton.setAlpha(0);
+        mDismissButton.setOnClickListener(v -> dismissPip());
+        findViewById(R.id.expand_button).setOnClickListener(v -> {
+            if (mMenuContainer.getAlpha() != 0) {
+                expandPip();
+            }
+        });
+        // TODO (b/161710689): Remove this once focusability for Windowless window is working
+        findViewById(R.id.expand_button).setFocusable(false);
+        mDismissButton.setFocusable(false);
+        mSettingsButton.setFocusable(false);
+
+        mResizeHandle = findViewById(R.id.resize_handle);
+        mResizeHandle.setAlpha(0);
+        mActionsGroup = findViewById(R.id.actions_group);
+        mBetweenActionPaddingLand = getResources().getDimensionPixelSize(
+                R.dimen.pip_between_action_padding_land);
+        mPipMenuIconsAlgorithm = new PipMenuIconsAlgorithm(mContext);
+        mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer,
+                mResizeHandle, mSettingsButton, mDismissButton);
+
+        initAccessibility();
+    }
+
+    private void initAccessibility() {
+        this.setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                String label = getResources().getString(R.string.pip_menu_title);
+                info.addAction(new AccessibilityNodeInfo.AccessibilityAction(ACTION_CLICK, label));
+            }
+
+            @Override
+            public boolean performAccessibilityAction(View host, int action, Bundle args) {
+                if (action == ACTION_CLICK && mMenuState == MENU_STATE_CLOSE) {
+                    mController.onPipShowMenu();
+                }
+                return super.performAccessibilityAction(host, action, args);
+            }
+        });
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_ESCAPE) {
+            hideMenu();
+            return true;
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (!mAllowTouches) {
+            return false;
+        }
+
+        if (mAllowMenuTimeout) {
+            repostDelayedHide(POST_INTERACTION_DISMISS_DELAY);
+        }
+
+        return super.dispatchTouchEvent(ev);
+    }
+
+    @Override
+    public boolean dispatchGenericMotionEvent(MotionEvent event) {
+        if (mAllowMenuTimeout) {
+            repostDelayedHide(POST_INTERACTION_DISMISS_DELAY);
+        }
+
+        return super.dispatchGenericMotionEvent(event);
+    }
+
+    void showMenu(int menuState, Rect stackBounds, boolean allowMenuTimeout,
+            boolean resizeMenuOnShow, boolean withDelay, boolean showResizeHandle) {
+        mAllowMenuTimeout = allowMenuTimeout;
+        if (mMenuState != menuState) {
+            // Disallow touches if the menu needs to resize while showing, and we are transitioning
+            // to/from a full menu state.
+            boolean disallowTouchesUntilAnimationEnd = resizeMenuOnShow
+                    && (mMenuState == MENU_STATE_FULL || menuState == MENU_STATE_FULL);
+            mAllowTouches = !disallowTouchesUntilAnimationEnd;
+            cancelDelayedHide();
+            updateActionViews(stackBounds);
+            if (mMenuContainerAnimator != null) {
+                mMenuContainerAnimator.cancel();
+            }
+            mMenuContainerAnimator = new AnimatorSet();
+            ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
+                    mMenuContainer.getAlpha(), 1f);
+            menuAnim.addUpdateListener(mMenuBgUpdateListener);
+            ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
+                    mSettingsButton.getAlpha(), 1f);
+            ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
+                    mDismissButton.getAlpha(), 1f);
+            ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
+                    mResizeHandle.getAlpha(),
+                    ENABLE_RESIZE_HANDLE && menuState == MENU_STATE_CLOSE && showResizeHandle
+                            ? 1f : 0f);
+            if (menuState == MENU_STATE_FULL) {
+                mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim,
+                        resizeAnim);
+            } else {
+                mMenuContainerAnimator.playTogether(dismissAnim, resizeAnim);
+            }
+            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_IN);
+            mMenuContainerAnimator.setDuration(menuState == MENU_STATE_CLOSE
+                    ? MENU_FADE_DURATION
+                    : MENU_SLOW_FADE_DURATION);
+            if (allowMenuTimeout) {
+                mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        repostDelayedHide(INITIAL_DISMISS_DELAY);
+                    }
+                });
+            }
+            if (withDelay) {
+                // starts the menu container animation after window expansion is completed
+                notifyMenuStateChange(menuState, resizeMenuOnShow, () -> {
+                    if (mMenuContainerAnimator == null) {
+                        return;
+                    }
+                    mMenuContainerAnimator.setStartDelay(MENU_SHOW_ON_EXPAND_START_DELAY);
+                    mMenuContainerAnimator.start();
+                });
+            } else {
+                notifyMenuStateChange(menuState, resizeMenuOnShow, null);
+                mMenuContainerAnimator.start();
+            }
+        } else {
+            // If we are already visible, then just start the delayed dismiss and unregister any
+            // existing input consumers from the previous drag
+            if (allowMenuTimeout) {
+                repostDelayedHide(POST_INTERACTION_DISMISS_DELAY);
+            }
+        }
+    }
+
+    /**
+     * Different from {@link #hideMenu()}, this function does not try to finish this menu activity
+     * and instead, it fades out the controls by setting the alpha to 0 directly without menu
+     * visibility callbacks invoked.
+     */
+    void fadeOutMenu() {
+        mMenuContainer.setAlpha(0f);
+        mSettingsButton.setAlpha(0f);
+        mDismissButton.setAlpha(0f);
+        mResizeHandle.setAlpha(0f);
+    }
+
+    void pokeMenu() {
+        cancelDelayedHide();
+    }
+
+    void onPipAnimationEnded() {
+        mAllowTouches = true;
+    }
+
+    void updateMenuLayout(Rect bounds) {
+        mPipMenuIconsAlgorithm.onBoundsChanged(bounds);
+    }
+
+    void hideMenu() {
+        hideMenu(null);
+    }
+
+    void hideMenu(Runnable animationEndCallback) {
+        hideMenu(animationEndCallback, true /* notifyMenuVisibility */, false);
+    }
+
+    private void hideMenu(final Runnable animationFinishedRunnable, boolean notifyMenuVisibility,
+            boolean animate) {
+        if (mMenuState != MENU_STATE_NONE) {
+            cancelDelayedHide();
+            if (notifyMenuVisibility) {
+                notifyMenuStateChange(MENU_STATE_NONE, mResize, null);
+            }
+            mMenuContainerAnimator = new AnimatorSet();
+            ObjectAnimator menuAnim = ObjectAnimator.ofFloat(mMenuContainer, View.ALPHA,
+                    mMenuContainer.getAlpha(), 0f);
+            menuAnim.addUpdateListener(mMenuBgUpdateListener);
+            ObjectAnimator settingsAnim = ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA,
+                    mSettingsButton.getAlpha(), 0f);
+            ObjectAnimator dismissAnim = ObjectAnimator.ofFloat(mDismissButton, View.ALPHA,
+                    mDismissButton.getAlpha(), 0f);
+            ObjectAnimator resizeAnim = ObjectAnimator.ofFloat(mResizeHandle, View.ALPHA,
+                    mResizeHandle.getAlpha(), 0f);
+            mMenuContainerAnimator.playTogether(menuAnim, settingsAnim, dismissAnim, resizeAnim);
+            mMenuContainerAnimator.setInterpolator(Interpolators.ALPHA_OUT);
+            mMenuContainerAnimator.setDuration(animate ? MENU_FADE_DURATION : 0);
+            mMenuContainerAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    if (animationFinishedRunnable != null) {
+                        animationFinishedRunnable.run();
+                    }
+                }
+            });
+            mMenuContainerAnimator.start();
+        }
+    }
+
+    void setActions(Rect stackBounds, List<RemoteAction> actions) {
+        mActions.clear();
+        mActions.addAll(actions);
+        updateActionViews(stackBounds);
+    }
+
+    private void updateActionViews(Rect stackBounds) {
+        ViewGroup expandContainer = findViewById(R.id.expand_container);
+        ViewGroup actionsContainer = findViewById(R.id.actions_container);
+        actionsContainer.setOnTouchListener((v, ev) -> {
+            // Do nothing, prevent click through to parent
+            return true;
+        });
+
+        if (mActions.isEmpty() || mMenuState == MENU_STATE_CLOSE) {
+            actionsContainer.setVisibility(View.INVISIBLE);
+        } else {
+            actionsContainer.setVisibility(View.VISIBLE);
+            if (mActionsGroup != null) {
+                // Ensure we have as many buttons as actions
+                final LayoutInflater inflater = LayoutInflater.from(mContext);
+                while (mActionsGroup.getChildCount() < mActions.size()) {
+                    final ImageButton actionView = (ImageButton) inflater.inflate(
+                            R.layout.pip_menu_action, mActionsGroup, false);
+                    mActionsGroup.addView(actionView);
+                }
+
+                // Update the visibility of all views
+                for (int i = 0; i < mActionsGroup.getChildCount(); i++) {
+                    mActionsGroup.getChildAt(i).setVisibility(i < mActions.size()
+                            ? View.VISIBLE
+                            : View.GONE);
+                }
+
+                // Recreate the layout
+                final boolean isLandscapePip = stackBounds != null
+                        && (stackBounds.width() > stackBounds.height());
+                for (int i = 0; i < mActions.size(); i++) {
+                    final RemoteAction action = mActions.get(i);
+                    final ImageButton actionView = (ImageButton) mActionsGroup.getChildAt(i);
+
+                    // TODO: Check if the action drawable has changed before we reload it
+                    action.getIcon().loadDrawableAsync(mContext, d -> {
+                        d.setTint(Color.WHITE);
+                        actionView.setImageDrawable(d);
+                    }, mHandler);
+                    actionView.setContentDescription(action.getContentDescription());
+                    if (action.isEnabled()) {
+                        actionView.setOnClickListener(v -> {
+                            mHandler.post(() -> {
+                                try {
+                                    action.getActionIntent().send();
+                                } catch (CanceledException e) {
+                                    Log.w(TAG, "Failed to send action", e);
+                                }
+                            });
+                        });
+                    }
+                    actionView.setEnabled(action.isEnabled());
+                    actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
+
+                    // Update the margin between actions
+                    LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
+                            actionView.getLayoutParams();
+                    lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0;
+                }
+            }
+
+            // Update the expand container margin to adjust the center of the expand button to
+            // account for the existence of the action container
+            FrameLayout.LayoutParams expandedLp =
+                    (FrameLayout.LayoutParams) expandContainer.getLayoutParams();
+            expandedLp.topMargin = getResources().getDimensionPixelSize(
+                    R.dimen.pip_action_padding);
+            expandedLp.bottomMargin = getResources().getDimensionPixelSize(
+                    R.dimen.pip_expand_container_edge_margin);
+            expandContainer.requestLayout();
+        }
+    }
+
+    void updateDismissFraction(float fraction) {
+        int alpha;
+        final float menuAlpha = 1 - fraction;
+        if (mMenuState == MENU_STATE_FULL) {
+            mMenuContainer.setAlpha(menuAlpha);
+            mSettingsButton.setAlpha(menuAlpha);
+            mDismissButton.setAlpha(menuAlpha);
+            final float interpolatedAlpha =
+                    MENU_BACKGROUND_ALPHA * menuAlpha + DISMISS_BACKGROUND_ALPHA * fraction;
+            alpha = (int) (interpolatedAlpha * 255);
+        } else {
+            if (mMenuState == MENU_STATE_CLOSE) {
+                mDismissButton.setAlpha(menuAlpha);
+            }
+            alpha = (int) (fraction * DISMISS_BACKGROUND_ALPHA * 255);
+        }
+        mBackgroundDrawable.setAlpha(alpha);
+    }
+
+    private void notifyMenuStateChange(int menuState, boolean resize, Runnable callback) {
+        mMenuState = menuState;
+        mController.onMenuStateChanged(menuState, resize, callback);
+    }
+
+    private void expandPip() {
+        // Do not notify menu visibility when hiding the menu, the controller will do this when it
+        // handles the message
+        hideMenu(mController::onPipExpand, false /* notifyMenuVisibility */, true /* animate */);
+    }
+
+    private void dismissPip() {
+        // Since tapping on the close-button invokes a double-tap wait callback in PipTouchHandler,
+        // we want to disable animating the fadeout animation of the buttons in order to call on
+        // PipTouchHandler#onPipDismiss fast enough.
+        final boolean animate = mMenuState != MENU_STATE_CLOSE;
+        // Do not notify menu visibility when hiding the menu, the controller will do this when it
+        // handles the message
+        hideMenu(mController::onPipDismiss, false /* notifyMenuVisibility */, animate);
+    }
+
+    private void showSettings() {
+        final Pair<ComponentName, Integer> topPipActivityInfo =
+                PipUtils.getTopPipActivity(mContext, ActivityManager.getService());
+        if (topPipActivityInfo.first != null) {
+            final UserHandle user = UserHandle.of(topPipActivityInfo.second);
+            final Intent settingsIntent = new Intent(ACTION_PICTURE_IN_PICTURE_SETTINGS,
+                    Uri.fromParts("package", topPipActivityInfo.first.getPackageName(), null));
+            settingsIntent.putExtra(Intent.EXTRA_USER_HANDLE, user);
+            settingsIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
+            mContext.startActivity(settingsIntent);
+        }
+    }
+
+    private void cancelDelayedHide() {
+        mHandler.removeCallbacks(mHideMenuRunnable);
+    }
+
+    private void repostDelayedHide(int delay) {
+        int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay,
+                FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS);
+        mHandler.removeCallbacks(mHideMenuRunnable);
+        mHandler.postDelayed(mHideMenuRunnable, recommendedTimeout);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 19138fdb..dcee2a5 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -23,6 +23,8 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Debug;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.Log;
 import android.view.Choreographer;
 
@@ -66,6 +68,8 @@
     private PipMenuActivityController mMenuController;
     private PipSnapAlgorithm mSnapAlgorithm;
 
+    private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+
     /** PIP's current bounds on the screen. */
     private final Rect mBounds = new Rect();
 
@@ -128,8 +132,10 @@
                         SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
 
     private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> {
-        mMenuController.updateMenuLayout(newBounds);
-        mBounds.set(newBounds);
+        mMainHandler.post(() -> {
+            mMenuController.updateMenuLayout(newBounds);
+            mBounds.set(newBounds);
+        });
     };
 
     /**
@@ -253,7 +259,9 @@
                 mTemporaryBounds.set(toBounds);
                 mPipTaskOrganizer.scheduleUserResizePip(mBounds, mTemporaryBounds,
                         (Rect newBounds) -> {
-                            mMenuController.updateMenuLayout(newBounds);
+                            mMainHandler.post(() -> {
+                                mMenuController.updateMenuLayout(newBounds);
+                            });
                     });
             }
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
index 2800bb9..f6853ec 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -111,6 +111,7 @@
     private InputMonitor mInputMonitor;
     private InputEventReceiver mInputEventReceiver;
     private PipTaskOrganizer mPipTaskOrganizer;
+    private PipMenuActivityController mPipMenuActivityController;
     private PipUiEventLogger mPipUiEventLogger;
 
     private int mCtrlType;
@@ -119,7 +120,7 @@
             PipMotionHelper motionHelper, DeviceConfigProxy deviceConfig,
             PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier,
             Runnable updateMovementBoundsRunnable, SysUiState sysUiState,
-            PipUiEventLogger pipUiEventLogger) {
+            PipUiEventLogger pipUiEventLogger, PipMenuActivityController menuActivityController) {
         mContext = context;
         mDisplayId = context.getDisplayId();
         mMainExecutor = context.getMainExecutor();
@@ -129,6 +130,7 @@
         mMovementBoundsSupplier = movementBoundsSupplier;
         mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable;
         mSysUiState = sysUiState;
+        mPipMenuActivityController = menuActivityController;
         mPipUiEventLogger = pipUiEventLogger;
 
         context.getDisplay().getRealSize(mMaxSize);
@@ -298,6 +300,7 @@
         float x = ev.getX();
         float y = ev.getY();
         if (action == MotionEvent.ACTION_DOWN) {
+            final Rect currentPipBounds = mMotionHelper.getBounds();
             mLastResizeBounds.setEmpty();
             mAllowGesture = isInValidSysUiState() && isWithinTouchRegion((int) x, (int) y);
             if (mAllowGesture) {
@@ -305,6 +308,10 @@
                 mDownPoint.set(x, y);
                 mLastDownBounds.set(mMotionHelper.getBounds());
             }
+            if (!currentPipBounds.contains((int) ev.getX(), (int) ev.getY())
+                    && mPipMenuActivityController.isMenuVisible()) {
+                mPipMenuActivityController.hideMenu();
+            }
 
         } else if (mAllowGesture) {
             switch (action) {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 1b84c14..9693f23 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -230,7 +230,7 @@
         mPipResizeGestureHandler =
                 new PipResizeGestureHandler(context, pipBoundsHandler, mMotionHelper,
                         deviceConfig, pipTaskOrganizer, this::getMovementBounds,
-                        this::updateMovementBounds, sysUiState, pipUiEventLogger);
+                        this::updateMovementBounds, sysUiState, pipUiEventLogger, menuController);
         mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler,
                 () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mMotionHelper.getBounds(),
                         true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()),
@@ -773,10 +773,7 @@
      * Updates the appearance of the menu and scrim on top of the PiP while dismissing.
      */
     private void updateDismissFraction() {
-        // Skip updating the dismiss fraction when the IME is showing. This is to work around an
-        // issue where starting the menu activity for the dismiss overlay will steal the window
-        // focus, which closes the IME.
-        if (mMenuController != null && !mIsImeShowing) {
+        if (mMenuController != null) {
             Rect bounds = mMotionHelper.getBounds();
             final float target = mInsetBounds.bottom;
             float fraction = 0f;
@@ -784,7 +781,7 @@
                 final float distance = bounds.bottom - target;
                 fraction = Math.min(distance / bounds.height(), 1f);
             }
-            if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuActivityVisible()) {
+            if (Float.compare(fraction, 0f) != 0 || mMenuController.isMenuVisible()) {
                 // Update if the fraction > 0, or if fraction == 0 and the menu was already visible
                 mMenuController.setDismissFraction(fraction);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 3aef0da..a388fa3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -60,12 +60,14 @@
 import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
@@ -140,7 +142,6 @@
     // Used to calculate the movement bounds
     private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
     private final Rect mTmpInsetBounds = new Rect();
-    private final Rect mTmpNormalBounds = new Rect();
 
     // Keeps track of the IME visibility to adjust the PiP when the IME is visible
     private boolean mImeVisible;
@@ -214,10 +215,8 @@
         public void onMovementBoundsChanged(boolean fromImeAdjustment) {
             mHandler.post(() -> {
                 // Populate the inset / normal bounds and DisplayInfo from mPipBoundsHandler first.
-                final Rect destinationBounds = new Rect();
-                mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mTmpNormalBounds,
-                        destinationBounds, mTmpDisplayInfo);
-                mDefaultPipBounds.set(destinationBounds);
+                mPipBoundsHandler.onMovementBoundsChanged(mTmpInsetBounds, mPipBounds,
+                        mDefaultPipBounds, mTmpDisplayInfo);
             });
         }
 
@@ -236,8 +235,9 @@
     public PipManager(Context context, BroadcastDispatcher broadcastDispatcher,
             ConfigurationController configController,
             DisplayController displayController,
-            Divider divider,
-            @NonNull PipUiEventLogger pipUiEventLogger) {
+            Optional<SplitScreen> splitScreenOptional,
+            @NonNull PipUiEventLogger pipUiEventLogger,
+            ShellTaskOrganizer shellTaskOrganizer) {
         if (mInitialized) {
             return;
         }
@@ -255,7 +255,8 @@
                 .getInteger(R.integer.config_pipResizeAnimationDuration);
         mPipSurfaceTransactionHelper = new PipSurfaceTransactionHelper(context, configController);
         mPipTaskOrganizer = new PipTaskOrganizer(mContext, mPipBoundsHandler,
-                mPipSurfaceTransactionHelper, divider, displayController, pipUiEventLogger);
+                mPipSurfaceTransactionHelper, splitScreenOptional, displayController,
+                pipUiEventLogger, shellTaskOrganizer);
         mPipTaskOrganizer.registerPipTransitionCallback(this);
         mActivityTaskManager = ActivityTaskManager.getService();
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
@@ -274,7 +275,6 @@
 
         try {
             WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener);
-            mPipTaskOrganizer.registerOrganizer(WINDOWING_MODE_PINNED);
         } catch (RemoteException | UnsupportedOperationException e) {
             Log.e(TAG, "Failed to register pinned stack listener", e);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
index 214088c..7037403 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
@@ -36,8 +36,8 @@
  * Activity to show the PIP menu to control PIP.
  */
 public class PipMenuActivity extends Activity implements PipManager.Listener {
-    private static final boolean DEBUG = false;
     private static final String TAG = "PipMenuActivity";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     static final String EXTRA_CUSTOM_ACTIONS = "custom_actions";
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
index 651a4f3..5e5de58 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java
@@ -114,6 +114,14 @@
                 notifyPipNotification();
             }
         }
+
+        @Override
+        public void onMetadataChanged(MediaMetadata metadata) {
+            if (updateMediaControllerMetadata() && mNotified) {
+                // update notification
+                notifyPipNotification();
+            }
+        }
     };
 
     private final PipManager.MediaListener mPipMediaListener = new PipManager.MediaListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
similarity index 61%
copy from packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
copy to packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
index 114c30e..8b8941a 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
+++ b/packages/SystemUI/src/com/android/systemui/power/dagger/PowerModule.java
@@ -14,17 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone.dagger;
+package com.android.systemui.power.dagger;
 
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import com.android.systemui.power.PowerNotificationWarnings;
+import com.android.systemui.power.PowerUI;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
+import dagger.Binds;
+import dagger.Module;
 
-import javax.inject.Qualifier;
 
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface PipMenuActivityClass {
+/** Dagger Module for code in the power package. */
+@Module
+public interface PowerModule {
+    /** */
+    @Binds
+    PowerUI.WarningsUI provideWarningsUi(PowerNotificationWarnings controllerImpl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index 255ba1b..e57478e 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -57,7 +57,8 @@
     @VisibleForTesting
     internal companion object {
         val OPS_MIC_CAMERA = intArrayOf(AppOpsManager.OP_CAMERA,
-                AppOpsManager.OP_RECORD_AUDIO)
+                AppOpsManager.OP_PHONE_CALL_CAMERA, AppOpsManager.OP_RECORD_AUDIO,
+                AppOpsManager.OP_PHONE_CALL_MICROPHONE)
         val OPS_LOCATION = intArrayOf(
                 AppOpsManager.OP_COARSE_LOCATION,
                 AppOpsManager.OP_FINE_LOCATION)
@@ -248,9 +249,11 @@
 
     private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
         val type: PrivacyType = when (appOpItem.code) {
+            AppOpsManager.OP_PHONE_CALL_CAMERA,
             AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
-            AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION
+            AppOpsManager.OP_COARSE_LOCATION,
             AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
+            AppOpsManager.OP_PHONE_CALL_MICROPHONE,
             AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
             else -> return null
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 8740581..8ff96c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -22,6 +22,7 @@
 
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.qs.AutoAddTracker;
+import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.statusbar.phone.AutoTileManager;
 import com.android.systemui.statusbar.phone.ManagedProfileController;
@@ -29,6 +30,7 @@
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.HotspotController;
 
+import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 
@@ -56,4 +58,9 @@
         manager.init();
         return manager;
     }
+
+
+    /** */
+    @Binds
+    QSHost provideQsHost(QSTileHost controllerImpl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index 0347867..1a7e229 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -38,7 +38,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.phone.StatusBar;
 
 import java.util.Optional;
@@ -56,7 +56,7 @@
     private final static String TAG = "OverviewProxyRecentsImpl";
     @Nullable
     private final Lazy<StatusBar> mStatusBarLazy;
-    private final Optional<Divider> mDividerOptional;
+    private final Optional<SplitScreen> mSplitScreenOptional;
 
     private Context mContext;
     private Handler mHandler;
@@ -66,9 +66,9 @@
     @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
     @Inject
     public OverviewProxyRecentsImpl(Optional<Lazy<StatusBar>> statusBarLazy,
-            Optional<Divider> dividerOptional) {
+            Optional<SplitScreen> splitScreenOptional) {
         mStatusBarLazy = statusBarLazy.orElse(null);
-        mDividerOptional = dividerOptional;
+        mSplitScreenOptional = splitScreenOptional;
     }
 
     @Override
@@ -163,12 +163,12 @@
             if (runningTask.supportsSplitScreenMultiWindow) {
                 if (ActivityManagerWrapper.getInstance().setTaskWindowingModeSplitScreenPrimary(
                         runningTask.id, stackCreateMode, initialBounds)) {
-                    mDividerOptional.ifPresent(Divider::onDockedTopTask);
-
-                    // The overview service is handling split screen, so just skip the wait for the
-                    // first draw and notify the divider to start animating now
-                    mDividerOptional.ifPresent(Divider::onRecentsDrawn);
-
+                    mSplitScreenOptional.ifPresent(splitScreen -> {
+                        splitScreen.onDockedTopTask();
+                        // The overview service is handling split screen, so just skip the wait
+                        // for the first draw and notify the divider to start animating now
+                        splitScreen.onRecentsDrawn();
+                    });
                     return true;
                 }
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index e931a6b..263bbdb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -86,7 +86,7 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputMonitorCompat;
 import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -98,6 +98,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
+import java.util.function.BiConsumer;
 
 import javax.inject.Inject;
 
@@ -123,7 +124,7 @@
     private final Context mContext;
     private final PipUI mPipUI;
     private final Optional<Lazy<StatusBar>> mStatusBarOptionalLazy;
-    private final Optional<Divider> mDividerOptional;
+    private final Optional<SplitScreen> mSplitScreenOptional;
     private SysUiState mSysUiState;
     private final Handler mHandler;
     private final Lazy<NavigationBarController> mNavBarControllerLazy;
@@ -232,7 +233,9 @@
             }
             long token = Binder.clearCallingIdentity();
             try {
-                mDividerOptional.ifPresent(Divider::onDockedFirstAnimationFrame);
+                mSplitScreenOptional.ifPresent(splitScreen -> {
+                    splitScreen.onDockedFirstAnimationFrame();
+                });
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -262,8 +265,8 @@
             }
             long token = Binder.clearCallingIdentity();
             try {
-                return mDividerOptional.map(
-                        divider -> divider.getView().getNonMinimizedSplitScreenSecondaryBounds())
+                return mSplitScreenOptional.map(splitScreen ->
+                        splitScreen.getDividerView().getNonMinimizedSplitScreenSecondaryBounds())
                         .orElse(null);
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -399,10 +402,8 @@
 
         @Override
         public void setSplitScreenMinimized(boolean minimized) {
-            Divider divider = mDividerOptional.get();
-            if (divider != null) {
-                divider.setMinimized(minimized);
-            }
+            mSplitScreenOptional.ifPresent(
+                    splitScreen -> splitScreen.setMinimized(minimized));
         }
 
         @Override
@@ -592,6 +593,8 @@
     };
 
     private final StatusBarWindowCallback mStatusBarWindowCallback = this::onStatusBarStateChanged;
+    private final BiConsumer<Rect, Rect> mSplitScreenBoundsChangeListener =
+            this::notifySplitScreenBoundsChanged;
 
     // This is the death handler for the binder from the launcher service
     private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
@@ -602,7 +605,7 @@
     public OverviewProxyService(Context context, CommandQueue commandQueue,
             Lazy<NavigationBarController> navBarControllerLazy, NavigationModeController navModeController,
             NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
-            PipUI pipUI, Optional<Divider> dividerOptional,
+            PipUI pipUI, Optional<SplitScreen> splitScreenOptional,
             Optional<Lazy<StatusBar>> statusBarOptionalLazy, OneHandedUI oneHandedUI,
             BroadcastDispatcher broadcastDispatcher) {
         super(broadcastDispatcher);
@@ -613,7 +616,6 @@
         mNavBarControllerLazy = navBarControllerLazy;
         mStatusBarWinController = statusBarWinController;
         mConnectionBackoffAttempts = 0;
-        mDividerOptional = dividerOptional;
         mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
                 com.android.internal.R.string.config_recentsComponentName));
         mQuickStepIntent = new Intent(ACTION_QUICKSTEP)
@@ -653,6 +655,10 @@
         });
         mCommandQueue = commandQueue;
 
+        splitScreenOptional.ifPresent(splitScreen ->
+                splitScreen.registerBoundsChangeListener(mSplitScreenBoundsChangeListener));
+        mSplitScreenOptional = splitScreenOptional;
+
         // Listen for user setup
         startTracking();
 
@@ -755,10 +761,8 @@
         startConnectionToCurrentUser();
 
         // Clean up the minimized state if launcher dies
-        Divider divider = mDividerOptional.get();
-        if (divider != null) {
-            divider.setMinimized(false);
-        }
+        mSplitScreenOptional.ifPresent(
+                splitScreen -> splitScreen.setMinimized(false));
     }
 
     public void startConnectionToCurrentUser() {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
index 1d29ac6..a641730 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImplementation.java
@@ -35,7 +35,6 @@
     default void showRecentApps(boolean triggeredFromAltTab) {}
     default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {}
     default void toggleRecentApps() {}
-    default void growRecents() {}
     default boolean splitPrimaryTask(int stackCreateMode, Rect initialBounds,
             int metricsDockAction) {
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 469c4a7..3bf118d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -21,7 +21,6 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -41,7 +40,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.LongRunning;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import java.io.IOException;
@@ -70,7 +69,6 @@
     private static final String ACTION_STOP_NOTIF =
             "com.android.systemui.screenrecord.STOP_FROM_NOTIF";
     private static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE";
-    private static final String ACTION_DELETE = "com.android.systemui.screenrecord.DELETE";
 
     private final RecordingController mController;
     private final KeyguardDismissUtil mKeyguardDismissUtil;
@@ -81,12 +79,12 @@
     private final Executor mLongExecutor;
     private final UiEventLogger mUiEventLogger;
     private final NotificationManager mNotificationManager;
-    private final CurrentUserContextTracker mUserContextTracker;
+    private final UserContextProvider mUserContextTracker;
 
     @Inject
     public RecordingService(RecordingController controller, @LongRunning Executor executor,
             UiEventLogger uiEventLogger, NotificationManager notificationManager,
-            CurrentUserContextTracker userContextTracker, KeyguardDismissUtil keyguardDismissUtil) {
+            UserContextProvider userContextTracker, KeyguardDismissUtil keyguardDismissUtil) {
         mController = controller;
         mLongExecutor = executor;
         mUiEventLogger = uiEventLogger;
@@ -122,7 +120,7 @@
         String action = intent.getAction();
         Log.d(TAG, "onStartCommand " + action);
 
-        int mCurrentUserId = mUserContextTracker.getCurrentUserContext().getUserId();
+        int mCurrentUserId = mUserContextTracker.getUserContext().getUserId();
         UserHandle currentUser = new UserHandle(mCurrentUserId);
         switch (action) {
             case ACTION_START:
@@ -138,7 +136,7 @@
                 setTapsVisible(mShowTaps);
 
                 mRecorder = new ScreenMediaRecorder(
-                        mUserContextTracker.getCurrentUserContext(),
+                        mUserContextTracker.getUserContext(),
                         mCurrentUserId,
                         mAudioSource,
                         this
@@ -158,7 +156,7 @@
                 // we want to post the notifications for that user, which is NOT current user
                 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                 if (userId == -1) {
-                    userId = mUserContextTracker.getCurrentUserContext().getUserId();
+                    userId = mUserContextTracker.getUserContext().getUserId();
                 }
                 Log.d(TAG, "notifying for user " + userId);
                 stopRecording(userId);
@@ -184,25 +182,6 @@
                 // Close quick shade
                 sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
                 break;
-            case ACTION_DELETE:
-                mKeyguardDismissUtil.executeWhenUnlocked(() -> {
-                    // Close quick shade
-                    sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
-                    ContentResolver resolver = getContentResolver();
-                    Uri uri = Uri.parse(intent.getStringExtra(EXTRA_PATH));
-                    resolver.delete(uri, null, null);
-
-                    Toast.makeText(
-                            this,
-                            R.string.screenrecord_delete_description,
-                            Toast.LENGTH_LONG).show();
-                    Log.d(TAG, "Deleted recording " + uri);
-
-                    // Remove notification
-                    mNotificationManager.cancelAsUser(null, NOTIFICATION_VIEW_ID, currentUser);
-                    return false;
-                }, false);
-                break;
         }
         return Service.START_STICKY;
     }
@@ -312,16 +291,6 @@
                         PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
                 .build();
 
-        Notification.Action deleteAction = new Notification.Action.Builder(
-                Icon.createWithResource(this, R.drawable.ic_screenrecord),
-                getResources().getString(R.string.screenrecord_delete_label),
-                PendingIntent.getService(
-                        this,
-                        REQUEST_CODE,
-                        getDeleteIntent(this, uri.toString()),
-                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE))
-                .build();
-
         Bundle extras = new Bundle();
         extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
                 getResources().getString(R.string.screenrecord_name));
@@ -335,7 +304,6 @@
                         viewIntent,
                         PendingIntent.FLAG_IMMUTABLE))
                 .addAction(shareAction)
-                .addAction(deleteAction)
                 .setAutoCancel(true)
                 .addExtras(extras);
 
@@ -414,11 +382,6 @@
                 .putExtra(EXTRA_PATH, path);
     }
 
-    private static Intent getDeleteIntent(Context context, String path) {
-        return new Intent(context, RecordingService.class).setAction(ACTION_DELETE)
-                .putExtra(EXTRA_PATH, path);
-    }
-
     @Override
     public void onInfo(MediaRecorder mr, int what, int extra) {
         Log.d(TAG, "Media recorder info: " + what);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
index dc47ab4..2b62a29 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordDialog.java
@@ -35,7 +35,7 @@
 import android.widget.Switch;
 
 import com.android.systemui.R;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -51,7 +51,7 @@
     private static final String TAG = "ScreenRecordDialog";
 
     private final RecordingController mController;
-    private final CurrentUserContextTracker mCurrentUserContextTracker;
+    private final UserContextProvider mUserContextProvider;
     private Switch mTapsSwitch;
     private Switch mAudioSwitch;
     private Spinner mOptions;
@@ -59,9 +59,9 @@
 
     @Inject
     public ScreenRecordDialog(RecordingController controller,
-            CurrentUserContextTracker currentUserContextTracker) {
+            UserContextProvider userContextProvider) {
         mController = controller;
-        mCurrentUserContextTracker = currentUserContextTracker;
+        mUserContextProvider = userContextProvider;
     }
 
     @Override
@@ -108,7 +108,7 @@
     }
 
     private void requestScreenCapture() {
-        Context userContext = mCurrentUserContextTracker.getCurrentUserContext();
+        Context userContext = mUserContextProvider.getUserContext();
         boolean showTaps = mTapsSwitch.isChecked();
         ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked()
                 ? (ScreenRecordingAudioSource) mOptions.getSelectedItem()
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index e24fbc6..7dd4edd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -573,7 +573,12 @@
 
     private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
             Insets screenInsets, boolean showFlash) {
-        dismissScreenshot("new screenshot requested", true);
+        if (mScreenshotLayout.isAttachedToWindow()) {
+            if (!mDismissAnimation.isRunning()) { // if we didn't already dismiss for another reason
+                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED);
+            }
+            dismissScreenshot("new screenshot requested", true);
+        }
 
         mScreenBitmap = screenshot;
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
index 20fa991..74e0229 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotEvent.java
@@ -22,6 +22,7 @@
 import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER;
 import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OTHER;
 import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW;
+import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_VENDOR_GESTURE;
 
 import com.android.internal.logging.UiEvent;
 import com.android.internal.logging.UiEventLogger;
@@ -37,6 +38,8 @@
     SCREENSHOT_REQUESTED_OVERVIEW(304),
     @UiEvent(doc = "screenshot requested from accessibility actions")
     SCREENSHOT_REQUESTED_ACCESSIBILITY_ACTIONS(382),
+    @UiEvent(doc = "screenshot requested from vendor gesture")
+    SCREENSHOT_REQUESTED_VENDOR_GESTURE(638),
     @UiEvent(doc = "screenshot requested (other)")
     SCREENSHOT_REQUESTED_OTHER(305),
     @UiEvent(doc = "screenshot was saved")
@@ -56,7 +59,9 @@
     @UiEvent(doc = "screenshot interaction timed out")
     SCREENSHOT_INTERACTION_TIMEOUT(310),
     @UiEvent(doc = "screenshot explicitly dismissed")
-    SCREENSHOT_EXPLICIT_DISMISSAL(311);
+    SCREENSHOT_EXPLICIT_DISMISSAL(311),
+    @UiEvent(doc = "screenshot reentered for new screenshot")
+    SCREENSHOT_REENTERED(640);
 
     private final int mId;
 
@@ -81,6 +86,8 @@
                 return ScreenshotEvent.SCREENSHOT_REQUESTED_OVERVIEW;
             case SCREENSHOT_ACCESSIBILITY_ACTIONS:
                 return ScreenshotEvent.SCREENSHOT_REQUESTED_ACCESSIBILITY_ACTIONS;
+            case SCREENSHOT_VENDOR_GESTURE:
+                return ScreenshotEvent.SCREENSHOT_REQUESTED_VENDOR_GESTURE;
             case SCREENSHOT_OTHER:
             default:
                 return ScreenshotEvent.SCREENSHOT_REQUESTED_OTHER;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
new file mode 100644
index 0000000..91ef3c3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot.dagger;
+
+import android.app.Service;
+
+import com.android.systemui.screenshot.TakeScreenshotService;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+
+/**
+ * Defines injectable resources for Screenshots
+ */
+@Module
+public abstract class ScreenshotModule {
+
+    /** */
+    @Binds
+    @IntoMap
+    @ClassKey(TakeScreenshotService.class)
+    public abstract Service bindTakeScreenshotService(TakeScreenshotService service);
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt
deleted file mode 100644
index d7c4caaa..0000000
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt
+++ /dev/null
@@ -1,74 +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 com.android.systemui.settings
-
-import android.content.ContentResolver
-import android.content.Context
-import android.os.UserHandle
-import androidx.annotation.VisibleForTesting
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.util.Assert
-import java.lang.IllegalStateException
-
-/**
- * Tracks a reference to the context for the current user
- *
- * Constructor is injected at SettingsModule
- */
-class CurrentUserContextTracker internal constructor(
-    private val sysuiContext: Context,
-    broadcastDispatcher: BroadcastDispatcher
-) : CurrentUserContentResolverProvider {
-    private val userTracker: CurrentUserTracker
-    private var initialized = false
-
-    private var _curUserContext: Context? = null
-    val currentUserContext: Context
-        get() {
-            if (!initialized) {
-                throw IllegalStateException("Must initialize before getting context")
-            }
-            return _curUserContext!!
-        }
-
-    override val currentUserContentResolver: ContentResolver
-        get() = currentUserContext.contentResolver
-
-    init {
-        userTracker = object : CurrentUserTracker(broadcastDispatcher) {
-            override fun onUserSwitched(newUserId: Int) {
-                handleUserSwitched(newUserId)
-            }
-        }
-    }
-
-    fun initialize() {
-        initialized = true
-        userTracker.startTracking()
-        _curUserContext = makeUserContext(userTracker.currentUserId)
-    }
-
-    @VisibleForTesting
-    fun handleUserSwitched(newUserId: Int) {
-        _curUserContext = makeUserContext(newUserId)
-    }
-
-    private fun makeUserContext(uid: Int): Context {
-        Assert.isMainThread()
-        return sysuiContext.createContextAsUser(UserHandle.of(uid), 0)
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt b/packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
rename to packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt
index 9d05843..e0c0c15 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt
@@ -18,7 +18,10 @@
 
 import android.content.ContentResolver
 
-interface CurrentUserContentResolverProvider {
+/**
+ * Implemented by [UserTrackerImpl].
+ */
+interface UserContentResolverProvider {
 
-    val currentUserContentResolver: ContentResolver
+    val userContentResolver: ContentResolver
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt b/packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt
similarity index 82%
copy from packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
copy to packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt
index 9d05843..27af152 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt
@@ -16,9 +16,11 @@
 
 package com.android.systemui.settings
 
-import android.content.ContentResolver
+import android.content.Context
 
-interface CurrentUserContentResolverProvider {
-
-    val currentUserContentResolver: ContentResolver
+/**
+ * Implemented by [UserTrackerImpl].
+ */
+interface UserContextProvider {
+    val userContext: Context
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
new file mode 100644
index 0000000..26d408f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.settings
+
+import android.content.Context
+import android.content.pm.UserInfo
+import android.os.UserHandle
+import java.util.concurrent.Executor
+
+/**
+ * User tracker for SystemUI.
+ *
+ * This tracker provides async access to current user information, as well as callbacks for
+ * user/profile change.
+ */
+interface UserTracker : UserContentResolverProvider, UserContextProvider {
+
+    /**
+     * Current user's id.
+     */
+    val userId: Int
+
+    /**
+     * [UserHandle] for current user
+     */
+    val userHandle: UserHandle
+
+    /**
+     * List of profiles associated with the current user.
+     */
+    val userProfiles: List<UserInfo>
+
+    /**
+     * Add a [Callback] to be notified of chances, on a particular [Executor]
+     */
+    fun addCallback(callback: Callback, executor: Executor)
+
+    /**
+     * Remove a [Callback] previously added.
+     */
+    fun removeCallback(callback: Callback)
+
+    /**
+     * Ćallback for notifying of changes.
+     */
+    interface Callback {
+
+        /**
+         * Notifies that the current user has changed.
+         */
+        @JvmDefault
+        fun onUserChanged(newUser: Int, userContext: Context) {}
+
+        /**
+         * Notifies that the current user's profiles have changed.
+         */
+        @JvmDefault
+        fun onProfilesChanged(profiles: List<UserInfo>) {}
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
new file mode 100644
index 0000000..4cc0eee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -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 com.android.systemui.settings
+
+import android.content.BroadcastReceiver
+import android.content.ContentResolver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.UserInfo
+import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
+import android.util.Log
+import androidx.annotation.GuardedBy
+import androidx.annotation.WorkerThread
+import com.android.systemui.Dumpable
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.Assert
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.lang.IllegalStateException
+import java.lang.ref.WeakReference
+import java.util.concurrent.Executor
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+
+/**
+ * SystemUI cache for keeping track of the current user and associated values.
+ *
+ * The values provided asynchronously are NOT copies, but shared among all requesters. Do not
+ * modify them.
+ *
+ * This class purposefully doesn't use [BroadcastDispatcher] in order to receive the broadcast as
+ * soon as possible (and reduce its dependency graph).
+ * Other classes that want to listen to the broadcasts listened here SHOULD
+ * subscribe to this class instead.
+ *
+ * @see UserTracker
+ *
+ * Class constructed and initialized in [SettingsModule].
+ */
+class UserTrackerImpl internal constructor(
+    private val context: Context,
+    private val userManager: UserManager,
+    private val dumpManager: DumpManager,
+    private val backgroundHandler: Handler
+) : UserTracker, Dumpable, BroadcastReceiver() {
+
+    companion object {
+        private const val TAG = "UserTrackerImpl"
+    }
+
+    var initialized = false
+        private set
+
+    private val mutex = Any()
+
+    override var userId: Int by SynchronizedDelegate(context.userId)
+        private set
+
+    override var userHandle: UserHandle by SynchronizedDelegate(context.user)
+        private set
+
+    override var userContext: Context by SynchronizedDelegate(context)
+        private set
+
+    override val userContentResolver: ContentResolver
+        get() = userContext.contentResolver
+
+    /**
+     * Returns a [List<UserInfo>] of all profiles associated with the current user.
+     *
+     * The list returned is not a copy, so a copy should be made if its elements need to be
+     * modified.
+     */
+    override var userProfiles: List<UserInfo> by SynchronizedDelegate(emptyList())
+        private set
+
+    @GuardedBy("callbacks")
+    private val callbacks: MutableList<DataItem> = ArrayList()
+
+    fun initialize(startingUser: Int) {
+        if (initialized) {
+            return
+        }
+        initialized = true
+        setUserIdInternal(startingUser)
+
+        val filter = IntentFilter().apply {
+            addAction(Intent.ACTION_USER_SWITCHED)
+            addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+            addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED)
+        }
+        context.registerReceiverForAllUsers(this, filter, null /* permission */, backgroundHandler)
+
+        dumpManager.registerDumpable(TAG, this)
+    }
+
+    override fun onReceive(context: Context, intent: Intent) {
+        when (intent.action) {
+            Intent.ACTION_USER_SWITCHED -> {
+                handleSwitchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL))
+            }
+            Intent.ACTION_MANAGED_PROFILE_AVAILABLE, Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE -> {
+                handleProfilesChanged()
+            }
+        }
+    }
+
+    private fun setUserIdInternal(user: Int): Pair<Context, List<UserInfo>> {
+        val profiles = userManager.getProfiles(user)
+        val handle = UserHandle(user)
+        val ctx = context.createContextAsUser(handle, 0)
+
+        synchronized(mutex) {
+            userId = user
+            userHandle = handle
+            userContext = ctx
+            userProfiles = profiles.map { UserInfo(it) }
+        }
+        return ctx to profiles
+    }
+
+    @WorkerThread
+    private fun handleSwitchUser(newUser: Int) {
+        Assert.isNotMainThread()
+        if (newUser == UserHandle.USER_NULL) {
+            Log.w(TAG, "handleSwitchUser - Couldn't get new id from intent")
+            return
+        }
+
+        if (newUser == userId) return
+        Log.i(TAG, "Switching to user $newUser")
+
+        val (ctx, profiles) = setUserIdInternal(newUser)
+
+        notifySubscribers {
+            onUserChanged(newUser, ctx)
+            onProfilesChanged(profiles)
+        }
+    }
+
+    @WorkerThread
+    private fun handleProfilesChanged() {
+        Assert.isNotMainThread()
+
+        val profiles = userManager.getProfiles(userId)
+        synchronized(mutex) {
+            userProfiles = profiles.map { UserInfo(it) } // save a "deep" copy
+        }
+        notifySubscribers {
+            onProfilesChanged(profiles)
+        }
+    }
+
+    override fun addCallback(callback: UserTracker.Callback, executor: Executor) {
+        synchronized(callbacks) {
+            callbacks.add(DataItem(WeakReference(callback), executor))
+        }
+    }
+
+    override fun removeCallback(callback: UserTracker.Callback) {
+        synchronized(callbacks) {
+            callbacks.removeIf { it.sameOrEmpty(callback) }
+        }
+    }
+
+    private inline fun notifySubscribers(crossinline action: UserTracker.Callback.() -> Unit) {
+        val list = synchronized(callbacks) {
+            callbacks.toList()
+        }
+        list.forEach {
+            if (it.callback.get() != null) {
+                it.executor.execute {
+                    it.callback.get()?.action()
+                }
+            }
+        }
+    }
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+        pw.println("Initialized: $initialized")
+        if (initialized) {
+            pw.println("userId: $userId")
+            val ids = userProfiles.map { it.id }
+            pw.println("userProfiles: $ids")
+        }
+        val list = synchronized(callbacks) {
+            callbacks.toList()
+        }
+        pw.println("Callbacks:")
+        list.forEach {
+            it.callback.get()?.let {
+                pw.println("  $it")
+            }
+        }
+    }
+
+    private class SynchronizedDelegate<T : Any>(
+        private var value: T
+    ) : ReadWriteProperty<UserTrackerImpl, T> {
+
+        @GuardedBy("mutex")
+        override fun getValue(thisRef: UserTrackerImpl, property: KProperty<*>): T {
+            if (!thisRef.initialized) {
+                throw IllegalStateException("Must initialize before getting ${property.name}")
+            }
+            return synchronized(thisRef.mutex) { value }
+        }
+
+        @GuardedBy("mutex")
+        override fun setValue(thisRef: UserTrackerImpl, property: KProperty<*>, value: T) {
+            synchronized(thisRef.mutex) { this.value = value }
+        }
+    }
+}
+
+private data class DataItem(
+    val callback: WeakReference<UserTracker.Callback>,
+    val executor: Executor
+) {
+    fun sameOrEmpty(other: UserTracker.Callback): Boolean {
+        return callback.get()?.equals(other) ?: true
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
index b1ed772..7084d3f 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
@@ -16,12 +16,18 @@
 
 package com.android.systemui.settings.dagger;
 
+import android.app.ActivityManager;
 import android.content.Context;
+import android.os.Handler;
+import android.os.UserManager;
 
-import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.settings.CurrentUserContentResolverProvider;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.settings.UserContentResolverProvider;
+import com.android.systemui.settings.UserContextProvider;
+import com.android.systemui.settings.UserTracker;
+import com.android.systemui.settings.UserTrackerImpl;
 
 import dagger.Binds;
 import dagger.Module;
@@ -33,22 +39,27 @@
 @Module
 public abstract class SettingsModule {
 
-    /**
-     * Provides and initializes a CurrentUserContextTracker
-     */
-    @SysUISingleton
-    @Provides
-    static CurrentUserContextTracker provideCurrentUserContextTracker(
-            Context context,
-            BroadcastDispatcher broadcastDispatcher) {
-        CurrentUserContextTracker tracker =
-                new CurrentUserContextTracker(context, broadcastDispatcher);
-        tracker.initialize();
-        return tracker;
-    }
 
     @Binds
     @SysUISingleton
-    abstract CurrentUserContentResolverProvider bindCurrentUserContentResolverTracker(
-            CurrentUserContextTracker tracker);
+    abstract UserContextProvider bindUserContextProvider(UserTracker tracker);
+
+    @Binds
+    @SysUISingleton
+    abstract UserContentResolverProvider bindUserContentResolverProvider(
+            UserTracker tracker);
+
+    @SysUISingleton
+    @Provides
+    static UserTracker provideUserTracker(
+            Context context,
+            UserManager userManager,
+            DumpManager dumpManager,
+            @Background Handler handler
+    ) {
+        int startingUser = ActivityManager.getCurrentUser();
+        UserTrackerImpl tracker = new UserTrackerImpl(context, userManager, dumpManager, handler);
+        tracker.initialize(startingUser);
+        return tracker;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index ee3303b..0d92d1e 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -30,8 +30,10 @@
 import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.recents.Recents;
-import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.stackdivider.DividerView;
+import com.android.systemui.stackdivider.SplitScreen;
+
+import java.util.Optional;
 
 import javax.inject.Inject;
 
@@ -43,7 +45,7 @@
         implements ShortcutKeyServiceProxy.Callbacks {
 
     private static final String TAG = "ShortcutKeyDispatcher";
-    private final Divider mDivider;
+    private final Optional<SplitScreen> mSplitScreenOptional;
     private final Recents mRecents;
 
     private ShortcutKeyServiceProxy mShortcutKeyServiceProxy = new ShortcutKeyServiceProxy(this);
@@ -58,14 +60,16 @@
     protected final long SC_DOCK_RIGHT = META_MASK | KeyEvent.KEYCODE_RIGHT_BRACKET;
 
     @Inject
-    public ShortcutKeyDispatcher(Context context, Divider divider, Recents recents) {
+    public ShortcutKeyDispatcher(Context context,
+            Optional<SplitScreen> splitScreenOptional, Recents recents) {
         super(context);
-        mDivider = divider;
+        mSplitScreenOptional = splitScreenOptional;
         mRecents = recents;
     }
 
     /**
      * Registers a shortcut key to window manager.
+     *
      * @param shortcutCode packed representation of shortcut key code and meta information
      */
     public void registerShortcutKey(long shortcutCode) {
@@ -92,24 +96,28 @@
     }
 
     private void handleDockKey(long shortcutCode) {
-        if (mDivider == null || !mDivider.isDividerVisible()) {
-            // Split the screen
-            mRecents.splitPrimaryTask((shortcutCode == SC_DOCK_LEFT)
-                    ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
-                    : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1);
-        } else {
-            // If there is already a docked window, we respond by resizing the docking pane.
-            DividerView dividerView = mDivider.getView();
-            DividerSnapAlgorithm snapAlgorithm = dividerView.getSnapAlgorithm();
-            int dividerPosition = dividerView.getCurrentPosition();
-            DividerSnapAlgorithm.SnapTarget currentTarget =
-                    snapAlgorithm.calculateNonDismissingSnapTarget(dividerPosition);
-            DividerSnapAlgorithm.SnapTarget target = (shortcutCode == SC_DOCK_LEFT)
-                    ? snapAlgorithm.getPreviousTarget(currentTarget)
-                    : snapAlgorithm.getNextTarget(currentTarget);
-            dividerView.startDragging(true /* animate */, false /* touching */);
-            dividerView.stopDragging(target.position, 0f, false /* avoidDismissStart */,
-                    true /* logMetrics */);
+        if (mSplitScreenOptional.isPresent()) {
+            SplitScreen splitScreen = mSplitScreenOptional.get();
+            if (splitScreen.isDividerVisible()) {
+                // If there is already a docked window, we respond by resizing the docking pane.
+                DividerView dividerView = splitScreen.getDividerView();
+                DividerSnapAlgorithm snapAlgorithm = dividerView.getSnapAlgorithm();
+                int dividerPosition = dividerView.getCurrentPosition();
+                DividerSnapAlgorithm.SnapTarget currentTarget =
+                        snapAlgorithm.calculateNonDismissingSnapTarget(dividerPosition);
+                DividerSnapAlgorithm.SnapTarget target = (shortcutCode == SC_DOCK_LEFT)
+                        ? snapAlgorithm.getPreviousTarget(currentTarget)
+                        : snapAlgorithm.getNextTarget(currentTarget);
+                dividerView.startDragging(true /* animate */, false /* touching */);
+                dividerView.stopDragging(target.position, 0f, false /* avoidDismissStart */,
+                        true /* logMetrics */);
+                return;
+            }
         }
+
+        // Split the screen
+        mRecents.splitPrimaryTask((shortcutCode == SC_DOCK_LEFT)
+                ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
+                : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
deleted file mode 100644
index e9c880e..0000000
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2015 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.stackdivider;
-
-import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.window.WindowContainerToken;
-
-import com.android.systemui.SystemUI;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.function.Consumer;
-
-/**
- * Controls the docked stack divider.
- */
-@SysUISingleton
-public class Divider extends SystemUI {
-    private final KeyguardStateController mKeyguardStateController;
-    private final DividerController mDividerController;
-
-    Divider(Context context, DividerController dividerController,
-            KeyguardStateController keyguardStateController) {
-        super(context);
-        mDividerController = dividerController;
-        mKeyguardStateController = keyguardStateController;
-    }
-
-    @Override
-    public void start() {
-        mDividerController.start();
-        // Hide the divider when keyguard is showing. Even though keyguard/statusbar is above
-        // everything, it is actually transparent except for notifications, so we still need to
-        // hide any surfaces that are below it.
-        // TODO(b/148906453): Figure out keyguard dismiss animation for divider view.
-        mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
-            @Override
-            public void onKeyguardShowingChanged() {
-                mDividerController.onKeyguardShowingChanged(mKeyguardStateController.isShowing());
-            }
-        });
-        // Don't initialize the divider or anything until we get the default display.
-
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(
-                new TaskStackChangeListener() {
-                    @Override
-                    public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
-                            boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
-                        if (!wasVisible || task.configuration.windowConfiguration.getWindowingMode()
-                                != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                                || !mDividerController.isSplitScreenSupported()) {
-                            return;
-                        }
-
-                        if (mDividerController.isMinimized()) {
-                            onUndockingTask();
-                        }
-                    }
-
-                    @Override
-                    public void onActivityForcedResizable(String packageName, int taskId,
-                            int reason) {
-                        mDividerController.onActivityForcedResizable(packageName, taskId, reason);
-                    }
-
-                    @Override
-                    public void onActivityDismissingDockedStack() {
-                        mDividerController.onActivityDismissingSplitScreen();
-                    }
-
-                    @Override
-                    public void onActivityLaunchOnSecondaryDisplayFailed() {
-                        mDividerController.onActivityLaunchOnSecondaryDisplayFailed();
-                    }
-                }
-        );
-    }
-
-    @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        mDividerController.dump(pw);
-    }
-
-    /** Switch to minimized state if appropriate. */
-    public void setMinimized(final boolean minimized) {
-        mDividerController.setMinimized(minimized);
-    }
-
-    public boolean isMinimized() {
-        return mDividerController.isMinimized();
-    }
-
-    public boolean isHomeStackResizable() {
-        return mDividerController.isHomeStackResizable();
-    }
-
-    /** Callback for undocking task. */
-    public void onUndockingTask() {
-        mDividerController.onUndockingTask();
-    }
-
-    public void onRecentsDrawn() {
-        mDividerController.onRecentsDrawn();
-    }
-
-    public void onDockedFirstAnimationFrame() {
-        mDividerController.onDockedFirstAnimationFrame();
-    }
-
-    public void onDockedTopTask() {
-        mDividerController.onDockedTopTask();
-    }
-
-    public void onAppTransitionFinished() {
-        mDividerController.onAppTransitionFinished();
-    }
-
-    public DividerView getView() {
-        return mDividerController.getDividerView();
-    }
-
-    /** @return the container token for the secondary split root task. */
-    public WindowContainerToken getSecondaryRoot() {
-        return mDividerController.getSecondaryRoot();
-    }
-
-    /** Register a listener that gets called whenever the existence of the divider changes */
-    public void registerInSplitScreenListener(Consumer<Boolean> listener) {
-        mDividerController.registerInSplitScreenListener(listener);
-    }
-
-    /** {@code true} if this is visible */
-    public boolean isDividerVisible() {
-        return mDividerController.isDividerVisible();
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
index c915f07..64ee7ed5 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
@@ -38,7 +38,7 @@
 
 class DividerImeController implements DisplayImeController.ImePositionProcessor {
     private static final String TAG = "DividerImeController";
-    private static final boolean DEBUG = DividerController.DEBUG;
+    private static final boolean DEBUG = SplitScreenController.DEBUG;
 
     private static final float ADJUSTED_NONFOCUS_DIM = 0.3f;
 
@@ -100,15 +100,15 @@
     }
 
     private DividerView getView() {
-        return mSplits.mDividerController.getDividerView();
+        return mSplits.mSplitScreenController.getDividerView();
     }
 
     private SplitDisplayLayout getLayout() {
-        return mSplits.mDividerController.getSplitLayout();
+        return mSplits.mSplitScreenController.getSplitLayout();
     }
 
     private boolean isDividerVisible() {
-        return mSplits.mDividerController.isDividerVisible();
+        return mSplits.mSplitScreenController.isDividerVisible();
     }
 
     private boolean getSecondaryHasFocus(int displayId) {
@@ -151,7 +151,7 @@
         mSecondaryHasFocus = getSecondaryHasFocus(displayId);
         final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
                 && !imeIsFloating && !getLayout().mDisplayLayout.isLandscape()
-                && !mSplits.mDividerController.isMinimized();
+                && !mSplits.mSplitScreenController.isMinimized();
         if (mLastAdjustTop < 0) {
             mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop;
         } else if (mLastAdjustTop != (imeShouldShow ? mShownTop : mHiddenTop)) {
@@ -236,7 +236,7 @@
                         SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
             }
 
-            if (!mSplits.mDividerController.getWmProxy().queueSyncTransactionIfWaiting(wct)) {
+            if (!mSplits.mSplitScreenController.getWmProxy().queueSyncTransactionIfWaiting(wct)) {
                 WindowOrganizer.applyTransaction(wct);
             }
         }
@@ -250,7 +250,7 @@
                         : DisplayImeController.ANIMATION_DURATION_HIDE_MS);
             }
         }
-        mSplits.mDividerController.setAdjustedForIme(mTargetShown && !mPaused);
+        mSplits.mSplitScreenController.setAdjustedForIme(mTargetShown && !mPaused);
     }
 
     public void updateAdjustForIme() {
@@ -402,7 +402,7 @@
             mTargetAdjusted = mPausedTargetAdjusted;
             updateDimTargets();
             final DividerView view = getView();
-            if ((mTargetAdjusted != mAdjusted) && !mSplits.mDividerController.isMinimized()
+            if ((mTargetAdjusted != mAdjusted) && !mSplits.mSplitScreenController.isMinimized()
                     && view != null) {
                 // End unminimize animations since they conflict with adjustment animations.
                 view.finishAnimations();
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
deleted file mode 100644
index 9c21397..0000000
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.stackdivider;
-
-import android.content.Context;
-import android.os.Handler;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.SystemWindows;
-import com.android.wm.shell.common.TransactionPool;
-
-import dagger.Module;
-import dagger.Provides;
-
-/**
- * Module which provides a Divider.
- */
-@Module
-public class DividerModule {
-    @SysUISingleton
-    @Provides
-    static Divider provideDivider(Context context, DisplayController displayController,
-            SystemWindows systemWindows, DisplayImeController imeController, @Main Handler handler,
-            KeyguardStateController keyguardStateController, TransactionPool transactionPool) {
-        // TODO(b/161116823): fetch DividerProxy from WM shell lib.
-        DividerController dividerController = new DividerController(context, displayController,
-                systemWindows, imeController, handler, transactionPool);
-        return new Divider(context, dividerController, keyguardStateController);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index e5c02d6..fdf24b1 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -62,10 +62,8 @@
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
 import com.android.internal.policy.DockedDividerUtils;
-import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 
 import java.util.function.Consumer;
@@ -76,7 +74,7 @@
 public class DividerView extends FrameLayout implements OnTouchListener,
         OnComputeInternalInsetsListener {
     private static final String TAG = "DividerView";
-    private static final boolean DEBUG = DividerController.DEBUG;
+    private static final boolean DEBUG = SplitScreenController.DEBUG;
 
     public interface DividerCallbacks {
         void onDraggingStart();
@@ -138,6 +136,7 @@
     private final Rect mOtherInsetRect = new Rect();
     private final Rect mLastResizeRect = new Rect();
     private final Rect mTmpRect = new Rect();
+    private SplitScreenController mSplitScreenController;
     private WindowManagerProxy mWindowManagerProxy;
     private DividerWindowManager mWindowManager;
     private VelocityTracker mVelocityTracker;
@@ -353,9 +352,11 @@
         }
     }
 
-    public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState,
+    void injectDependencies(SplitScreenController splitScreenController,
+            DividerWindowManager windowManager, DividerState dividerState,
             DividerCallbacks callback, SplitScreenTaskOrganizer tiles, SplitDisplayLayout sdl,
             DividerImeController imeController, WindowManagerProxy wmProxy) {
+        mSplitScreenController = splitScreenController;
         mWindowManager = windowManager;
         mState = dividerState;
         mCallback = callback;
@@ -697,8 +698,7 @@
                 mTmpRect.top = 0;
                 break;
         }
-        Dependency.get(OverviewProxyService.class)
-                .notifySplitScreenBoundsChanged(mOtherTaskRect, mTmpRect);
+        mSplitScreenController.notifyBoundsChanged(mOtherTaskRect, mTmpRect);
     }
 
     private void cancelFlingAnimation() {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index ff8bab0..4c26694 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -69,9 +69,9 @@
     }
 
     public ForcedResizableInfoActivityController(Context context,
-            DividerController dividerController) {
+            SplitScreenController splitScreenController) {
         mContext = context;
-        dividerController.registerInSplitScreenListener(mDockedStackExistsListener);
+        splitScreenController.registerInSplitScreenListener(mDockedStackExistsListener);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreen.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreen.java
new file mode 100644
index 0000000..93b1f86
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreen.java
@@ -0,0 +1,91 @@
+/*
+ * 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.stackdivider;
+
+import android.graphics.Rect;
+import android.window.WindowContainerToken;
+
+import java.io.PrintWriter;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+/**
+ * Interface to engage split screen feature.
+ */
+public interface SplitScreen {
+    /** Returns {@code true} if split screen is supported on the device. */
+    boolean isSplitScreenSupported();
+
+    /** Called when keyguard showing state changed. */
+    void onKeyguardVisibilityChanged(boolean isShowing);
+
+    /** Returns {@link DividerView}. */
+    DividerView getDividerView();
+
+    /** Returns {@code true} if one of the split screen is in minimized mode. */
+    boolean isMinimized();
+
+    /** Returns {@code true} if the home stack is resizable. */
+    boolean isHomeStackResizable();
+
+    /** Returns {@code true} if the divider is visible. */
+    boolean isDividerVisible();
+
+    /** Switch to minimized state if appropriate. */
+    void setMinimized(boolean minimized);
+
+    /**
+     * Workaround for b/62528361, at the time recents has drawn, it may happen before a
+     * configuration change to the Divider, and internally, the event will be posted to the
+     * subscriber, or DividerView, which has been removed and prevented from resizing. Instead,
+     * register the event handler here and proxy the event to the current DividerView.
+     */
+    void onRecentsDrawn();
+
+    /** Called when there's an activity forced resizable. */
+    void onActivityForcedResizable(String packageName, int taskId, int reason);
+
+    /** Called when there's an activity dismissing split screen. */
+    void onActivityDismissingSplitScreen();
+
+    /** Called when there's an activity launch on secondary display failed. */
+    void onActivityLaunchOnSecondaryDisplayFailed();
+
+    /** Called when there's a task undocking. */
+    void onUndockingTask();
+
+    /** Called when the first docked animation frame rendered. */
+    void onDockedFirstAnimationFrame();
+
+    /** Called when top task docked. */
+    void onDockedTopTask();
+
+    /** Called when app transition finished. */
+    void onAppTransitionFinished();
+
+    /** Dumps current status of Split Screen. */
+    void dump(PrintWriter pw);
+
+    /** Registers listener that gets called whenever the existence of the divider changes. */
+    void registerInSplitScreenListener(Consumer<Boolean> listener);
+
+    /** Registers listener that gets called whenever the split screen bounds changes. */
+    void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener);
+
+    /** @return the container token for the secondary split root task. */
+    WindowContainerToken getSecondaryRoot();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenController.java
similarity index 91%
rename from packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
rename to packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenController.java
index 1ee8abb..360b495 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenController.java
@@ -35,6 +35,7 @@
 
 import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.systemui.R;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayChangeController;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
@@ -45,29 +46,40 @@
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
 /**
- * Controls the docked stack divider.
+ * Controls split screen feature.
  */
-public class DividerController implements DisplayController.OnDisplaysChangedListener {
+public class SplitScreenController implements SplitScreen,
+        DisplayController.OnDisplaysChangedListener {
     static final boolean DEBUG = false;
-    private static final String TAG = "Divider";
 
-    static final int DEFAULT_APP_TRANSITION_DURATION = 336;
+    private static final String TAG = "Divider";
+    private static final int DEFAULT_APP_TRANSITION_DURATION = 336;
+
+    private final Context mContext;
+    private final DisplayChangeController.OnDisplayChangingListener mRotationController;
+    private final DisplayController mDisplayController;
+    private final DisplayImeController mImeController;
+    private final DividerImeController mImePositionProcessor;
+    private final DividerState mDividerState = new DividerState();
+    private final ForcedResizableInfoActivityController mForcedResizableController;
+    private final Handler mHandler;
+    private final SplitScreenTaskOrganizer mSplits;
+    private final SystemWindows mSystemWindows;
+    final TransactionPool mTransactionPool;
+    private final WindowManagerProxy mWindowManagerProxy;
+
+    private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners =
+            new ArrayList<>();
+    private final ArrayList<WeakReference<BiConsumer<Rect, Rect>>> mBoundsChangedListeners =
+            new ArrayList<>();
+
 
     private DividerWindowManager mWindowManager;
     private DividerView mView;
-    private final DividerState mDividerState = new DividerState();
-    private boolean mVisible = false;
-    private boolean mMinimized = false;
-    private boolean mAdjustedForIme = false;
-    private boolean mHomeStackResizable = false;
-    private ForcedResizableInfoActivityController mForcedResizableController;
-    private SystemWindows mSystemWindows;
-    private DisplayController mDisplayController;
-    private DisplayImeController mImeController;
-    final TransactionPool mTransactionPool;
 
     // Keeps track of real-time split geometry including snap positions and ime adjustments
     private SplitDisplayLayout mSplitLayout;
@@ -77,21 +89,16 @@
     // layout that we sent back to WM.
     private SplitDisplayLayout mRotateSplitLayout;
 
-    private final Handler mHandler;
-    private final WindowManagerProxy mWindowManagerProxy;
-
-    private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners =
-            new ArrayList<>();
-
-    private final SplitScreenTaskOrganizer mSplits;
-    private final DisplayChangeController.OnDisplayChangingListener mRotationController;
-    private final DividerImeController mImePositionProcessor;
-    private final Context mContext;
     private boolean mIsKeyguardShowing;
+    private boolean mVisible = false;
+    private boolean mMinimized = false;
+    private boolean mAdjustedForIme = false;
+    private boolean mHomeStackResizable = false;
 
-    public DividerController(Context context,
+    public SplitScreenController(Context context,
             DisplayController displayController, SystemWindows systemWindows,
-            DisplayImeController imeController, Handler handler, TransactionPool transactionPool) {
+            DisplayImeController imeController, Handler handler, TransactionPool transactionPool,
+            ShellTaskOrganizer shellTaskOrganizer) {
         mContext = context;
         mDisplayController = displayController;
         mSystemWindows = systemWindows;
@@ -100,7 +107,7 @@
         mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
         mTransactionPool = transactionPool;
         mWindowManagerProxy = new WindowManagerProxy(mTransactionPool, mHandler);
-        mSplits = new SplitScreenTaskOrganizer(this);
+        mSplits = new SplitScreenTaskOrganizer(this, shellTaskOrganizer);
         mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler);
         mRotationController =
                 (display, fromRotation, toRotation, wct) -> {
@@ -140,10 +147,7 @@
                         wct.merge(t, true /* transfer */);
                     }
                 };
-    }
 
-    /** Inits the divider service. */
-    public void start() {
         mWindowManager = new DividerWindowManager(mSystemWindows);
         mDisplayController.addDisplayWindowListener(this);
         // Don't initialize the divider or anything until we get the default display.
@@ -155,15 +159,15 @@
     }
 
     /** Called when keyguard showing state changed. */
-    public void onKeyguardShowingChanged(boolean isShowing) {
+    public void onKeyguardVisibilityChanged(boolean showing) {
         if (!isSplitActive() || mView == null) {
             return;
         }
-        mView.setHidden(isShowing);
-        if (!isShowing) {
+        mView.setHidden(showing);
+        if (!showing) {
             mImePositionProcessor.updateAdjustForIme();
         }
-        mIsKeyguardShowing = isShowing;
+        mIsKeyguardShowing = showing;
     }
 
     @Override
@@ -256,8 +260,8 @@
         mView = (DividerView)
                 LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
         DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
-        mView.injectDependencies(mWindowManager, mDividerState, mForcedResizableController, mSplits,
-                mSplitLayout, mImePositionProcessor, mWindowManagerProxy);
+        mView.injectDependencies(this, mWindowManager, mDividerState, mForcedResizableController,
+                mSplits, mSplitLayout, mImePositionProcessor, mWindowManagerProxy);
         mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
         mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */);
         final int size = dctx.getResources().getDimensionPixelSize(
@@ -465,6 +469,24 @@
         }
     }
 
+    @Override
+    public void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) {
+        synchronized (mBoundsChangedListeners) {
+            mBoundsChangedListeners.add(new WeakReference<>(listener));
+        }
+    }
+
+    /** Notifies the bounds of split screen changed. */
+    void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) {
+        synchronized (mBoundsChangedListeners) {
+            mBoundsChangedListeners.removeIf(wf -> {
+                BiConsumer<Rect, Rect> l = wf.get();
+                if (l != null) l.accept(secondaryWindowBounds, secondaryWindowInsets);
+                return l == null;
+            });
+        }
+    }
+
     void startEnterSplit() {
         update(mDisplayController.getDisplayContext(
                 mContext.getDisplayId()).getResources().getConfiguration());
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index ef5e8a1..325c559 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -33,9 +33,13 @@
 import android.view.SurfaceSession;
 import android.window.TaskOrganizer;
 
-class SplitScreenTaskOrganizer extends TaskOrganizer {
+import com.android.wm.shell.ShellTaskOrganizer;
+
+class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener {
     private static final String TAG = "SplitScreenTaskOrg";
-    private static final boolean DEBUG = DividerController.DEBUG;
+    private static final boolean DEBUG = SplitScreenController.DEBUG;
+
+    private final ShellTaskOrganizer mTaskOrganizer;
 
     RunningTaskInfo mPrimary;
     RunningTaskInfo mSecondary;
@@ -44,18 +48,20 @@
     SurfaceControl mPrimaryDim;
     SurfaceControl mSecondaryDim;
     Rect mHomeBounds = new Rect();
-    final DividerController mDividerController;
+    final SplitScreenController mSplitScreenController;
     private boolean mSplitScreenSupported = false;
 
     final SurfaceSession mSurfaceSession = new SurfaceSession();
 
-    SplitScreenTaskOrganizer(DividerController dividerController) {
-        mDividerController = dividerController;
+    SplitScreenTaskOrganizer(SplitScreenController splitScreenController,
+                    ShellTaskOrganizer shellTaskOrganizer) {
+        mSplitScreenController = splitScreenController;
+        mTaskOrganizer = shellTaskOrganizer;
+        mTaskOrganizer.addListener(this, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
     }
 
     void init() throws RemoteException {
-        registerOrganizer(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        registerOrganizer(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
         synchronized (this) {
             try {
                 mPrimary = TaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY,
@@ -64,7 +70,7 @@
                         WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
             } catch (Exception e) {
                 // teardown to prevent callbacks
-                unregisterOrganizer();
+                mTaskOrganizer.removeListener(this);
                 throw e;
             }
         }
@@ -75,11 +81,11 @@
     }
 
     SurfaceControl.Transaction getTransaction() {
-        return mDividerController.mTransactionPool.acquire();
+        return mSplitScreenController.mTransactionPool.acquire();
     }
 
     void releaseTransaction(SurfaceControl.Transaction t) {
-        mDividerController.mTransactionPool.release(t);
+        mSplitScreenController.mTransactionPool.release(t);
     }
 
     @Override
@@ -140,7 +146,7 @@
                 t.apply();
                 releaseTransaction(t);
 
-                mDividerController.onTaskVanished();
+                mSplitScreenController.onTaskVanished();
             }
         }
     }
@@ -150,7 +156,7 @@
         if (taskInfo.displayId != DEFAULT_DISPLAY) {
             return;
         }
-        mDividerController.post(() -> handleTaskInfoChanged(taskInfo));
+        mSplitScreenController.post(() -> handleTaskInfoChanged(taskInfo));
     }
 
     /**
@@ -169,7 +175,7 @@
         }
         final boolean secondaryImpliedMinimize = mSecondary.topActivityType == ACTIVITY_TYPE_HOME
                 || (mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS
-                        && mDividerController.isHomeStackResizable());
+                        && mSplitScreenController.isHomeStackResizable());
         final boolean primaryWasEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
         final boolean secondaryWasEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
         if (info.token.asBinder() == mPrimary.token.asBinder()) {
@@ -181,7 +187,7 @@
         final boolean secondaryIsEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
         final boolean secondaryImpliesMinimize = mSecondary.topActivityType == ACTIVITY_TYPE_HOME
                 || (mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS
-                        && mDividerController.isHomeStackResizable());
+                        && mSplitScreenController.isHomeStackResizable());
         if (DEBUG) {
             Log.d(TAG, "onTaskInfoChanged " + mPrimary + "  " + mSecondary);
         }
@@ -197,14 +203,14 @@
                 Log.d(TAG, " at-least one split empty " + mPrimary.topActivityType
                         + "  " + mSecondary.topActivityType);
             }
-            if (mDividerController.isDividerVisible()) {
+            if (mSplitScreenController.isDividerVisible()) {
                 // Was in split-mode, which means we are leaving split, so continue that.
                 // This happens when the stack in the primary-split is dismissed.
                 if (DEBUG) {
                     Log.d(TAG, "    was in split, so this means leave it "
                             + mPrimary.topActivityType + "  " + mSecondary.topActivityType);
                 }
-                mDividerController.startDismissSplit();
+                mSplitScreenController.startDismissSplit();
             } else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) {
                 // Wasn't in split-mode (both were empty), but now that the primary split is
                 // populated, we should fully enter split by moving everything else into secondary.
@@ -213,15 +219,15 @@
                 if (DEBUG) {
                     Log.d(TAG, "   was not in split, but primary is populated, so enter it");
                 }
-                mDividerController.startEnterSplit();
+                mSplitScreenController.startEnterSplit();
             }
         } else if (secondaryImpliesMinimize) {
             // Both splits are populated but the secondary split has a home/recents stack on top,
             // so enter minimized mode.
-            mDividerController.ensureMinimizedSplit();
+            mSplitScreenController.ensureMinimizedSplit();
         } else {
             // Both splits are populated by normal activities, so make sure we aren't minimized.
-            mDividerController.ensureNormalSplit();
+            mSplitScreenController.ensureNormalSplit();
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
index f2500e5..13ed02e 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
@@ -33,7 +33,7 @@
  * Helper for serializing sync-transactions and corresponding callbacks.
  */
 class SyncTransactionQueue {
-    private static final boolean DEBUG = DividerController.DEBUG;
+    private static final boolean DEBUG = SplitScreenController.DEBUG;
     private static final String TAG = "SyncTransactionQueue";
 
     // Just a little longer than the sync-engine timeout of 5s
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
index eca4c80..0df69a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlertingNotificationManager.java
@@ -185,6 +185,7 @@
         mAlertEntries.put(entry.getKey(), alertEntry);
         onAlertEntryAdded(alertEntry);
         entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+        entry.setIsAlerting(true);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
index 4fa7822..e61e05a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -163,7 +165,7 @@
         if (!mDragDownCallback.isFalsingCheckNeeded()) {
             return false;
         }
-        return mFalsingManager.isFalseTouch() || !mDraggedFarEnough;
+        return mFalsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN) || !mDraggedFarEnough;
     }
 
     private void captureStartingChild(float x, float y) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 1079f10..17bf346 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -55,14 +55,6 @@
     void updateNotificationViews(String reason);
 
     /**
-     * Returns the maximum number of notifications to show while locked.
-     *
-     * @param recompute whether something has changed that means we should recompute this value
-     * @return the maximum number of notifications to show while locked
-     */
-    int getMaxNotificationsWhileLocked(boolean recompute);
-
-    /**
      * Called when the row states are updated by {@link NotificationViewHierarchyManager}.
      */
     void onUpdateRowStates();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index 1fd0b03..24515f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -164,6 +164,13 @@
     default void setRequestTopUi(boolean requestTopUi, String componentTag) {}
 
     /**
+     * Under low light conditions, we might want to increase the display brightness on devices that
+     * don't have an IR camera.
+     * @param brightness float from 0 to 1 or {@code LayoutParams.BRIGHTNESS_OVERRIDE_NONE}
+     */
+    default void setFaceAuthDisplayBrightness(float brightness) {}
+
+    /**
      * Custom listener to pipe data back to plugins about whether or not the status bar would be
      * collapsed if not for the plugin.
      * TODO: Find cleaner way to do this.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
index 77abcfa..1e935c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
@@ -85,6 +85,10 @@
         return mView.getShelfIcons();
     }
 
+    public @View.Visibility int getVisibility() {
+        return mView.getVisibility();
+    };
+
     public void setCollapsedIcons(NotificationIconContainer notificationIcons) {
         mView.setCollapsedIcons(notificationIcons);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 1cd1b60..852c055 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -420,11 +420,6 @@
 
         int visibleNotifications = 0;
         boolean onKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
-        int maxNotifications = -1;
-        if (onKeyguard && !mBypassController.getBypassEnabled()) {
-            maxNotifications = mPresenter.getMaxNotificationsWhileLocked(true /* recompute */);
-        }
-        mListContainer.setMaxDisplayedNotifications(maxNotifications);
         Stack<ExpandableNotificationRow> stack = new Stack<>();
         for (int i = N - 1; i >= 0; i--) {
             View child = mListContainer.getContainerChildAt(i);
@@ -439,8 +434,6 @@
             boolean isChildNotification =
                     mGroupManager.isChildInGroupWithSummary(entry.getSbn());
 
-            row.setOnKeyguard(onKeyguard);
-
             if (!onKeyguard) {
                 // If mAlwaysExpandNonGroupedNotification is false, then only expand the
                 // very first notification and if it's not a child of grouped notifications.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index ba54d1b..6fa3633 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.Gefingerpoken
 import com.android.systemui.Interpolators
 import com.android.systemui.R
+import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.plugins.FalsingManager
 import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -106,7 +107,7 @@
     private var velocityTracker: VelocityTracker? = null
 
     private val isFalseTouch: Boolean
-        get() = falsingManager.isFalseTouch
+        get() = falsingManager.isFalseTouch(NOTIFICATION_DRAG_DOWN)
     var qsExpanded: Boolean = false
     var pulseExpandAbortListener: Runnable? = null
     var bouncerShowing: Boolean = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
index 44550b7..db2875a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -37,6 +37,8 @@
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.StatusBarStateControllerImpl;
+import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
 import com.android.systemui.statusbar.notification.DynamicChildBindController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -45,13 +47,19 @@
 import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.ManagedProfileController;
+import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
+import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
 import com.android.systemui.statusbar.policy.RemoteInputUriController;
 import com.android.systemui.tracing.ProtoTracer;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.concurrency.DelayableExecutor;
 
+import dagger.Binds;
 import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
@@ -136,6 +144,12 @@
         return new SmartReplyController(entryManager, statusBarService, clickNotifier);
     }
 
+
+    /** */
+    @Binds
+    NotificationRemoteInputManager.Callback provideNotificationRemoteInputManagerCallback(
+            StatusBarRemoteInputCallback callbackImpl);
+
     /** */
     @SysUISingleton
     @Provides
@@ -179,4 +193,22 @@
     static CommandQueue provideCommandQueue(Context context, ProtoTracer protoTracer) {
         return new CommandQueue(context, protoTracer);
     }
+
+    /**
+     */
+    @Binds
+    ManagedProfileController provideManagedProfileController(
+            ManagedProfileControllerImpl controllerImpl);
+
+    /**
+     */
+    @Binds
+    SysuiStatusBarStateController providesSysuiStatusBarStateController(
+            StatusBarStateControllerImpl statusBarStateControllerImpl);
+
+    /**
+     */
+    @Binds
+    StatusBarIconController provideStatusBarIconController(
+            StatusBarIconControllerImpl controllerImpl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index ea614fb..838cf0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -55,12 +55,13 @@
 import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.UiBackground;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.NotificationChannels;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -80,13 +81,13 @@
     private final CommandQueue mCommandQueue;
     private boolean mDockedStackExists;
     private KeyguardStateController mKeyguardStateController;
-    private final Divider mDivider;
+    private final Optional<SplitScreen> mSplitScreenOptional;
 
     @Inject
     public InstantAppNotifier(Context context, CommandQueue commandQueue,
-            @UiBackground Executor uiBgExecutor, Divider divider) {
+            @UiBackground Executor uiBgExecutor, Optional<SplitScreen> splitScreenOptional) {
         super(context);
-        mDivider = divider;
+        mSplitScreenOptional = splitScreenOptional;
         mCommandQueue = commandQueue;
         mUiBgExecutor = uiBgExecutor;
     }
@@ -105,11 +106,11 @@
         mCommandQueue.addCallback(this);
         mKeyguardStateController.addCallback(this);
 
-        mDivider.registerInSplitScreenListener(
-                exists -> {
+        mSplitScreenOptional.ifPresent(splitScreen ->
+                splitScreen.registerInSplitScreenListener(exists -> {
                     mDockedStackExists = exists;
                     updateForegroundInstantApps();
-                });
+                }));
 
         // Clear out all old notifications on startup (only present in the case where sysui dies)
         NotificationManager noMan = mContext.getSystemService(NotificationManager.class);
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 387247e..8ce9d94 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
@@ -177,6 +177,7 @@
     @Nullable private Long mPendingAnimationDuration;
     private boolean mIsMarkedForUserTriggeredMovement;
     private boolean mShelfIconVisible;
+    private boolean mIsAlerting;
 
     /**
      * @param sbn the StatusBarNotification from system server
@@ -955,6 +956,14 @@
         mIsMarkedForUserTriggeredMovement = marked;
     }
 
+    public void setIsAlerting(boolean isAlerting) {
+        mIsAlerting = isAlerting;
+    }
+
+    public boolean isAlerting() {
+        return mIsAlerting;
+    }
+
     /** Information about a suggestion that is being edited. */
     public static class EditedSuggestionInfo {
 
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 7c061aa..e5425cf 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
@@ -140,7 +140,7 @@
                                         .build();
                         ExpandableNotificationRowController rowController =
                                 component.getExpandableNotificationRowController();
-                        rowController.init();
+                        rowController.init(entry);
                         entry.setRowController(rowController);
                         bindRow(entry, row);
                         updateRow(entry, row);
@@ -160,7 +160,6 @@
         mNotificationRemoteInputManager.bindRow(row);
         row.setOnActivatedListener(mPresenter);
         entry.setRow(row);
-        row.setEntry(entry);
         mNotifBindPipeline.manageRow(entry, row);
         mBindRowCallback.onBindRow(row);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 6d01324..f3ed95b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -34,7 +34,7 @@
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
@@ -127,7 +127,7 @@
             LauncherApps launcherApps,
             ShortcutManager shortcutManager,
             ChannelEditorDialogController channelEditorDialogController,
-            CurrentUserContextTracker contextTracker,
+            UserContextProvider contextTracker,
             Provider<PriorityOnboardingDialogController.Builder> builderProvider,
             AssistantFeedbackController assistantFeedbackController,
             BubbleController bubbleController,
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 9c09cba..46b4973 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
@@ -141,7 +141,7 @@
         void onExpansionChanged(boolean isExpanded);
     }
 
-    private StatusBarStateController mStatusbarStateController;
+    private StatusBarStateController mStatusBarStateController;
     private KeyguardBypassController mBypassController;
     private LayoutListener mLayoutListener;
     private RowContentBindStage mRowContentBindStage;
@@ -463,16 +463,6 @@
     }
 
     /**
-     * Set the entry for the row.
-     *
-     * @param entry the entry this row is tied to
-     */
-    public void setEntry(@NonNull NotificationEntry entry) {
-        mEntry = entry;
-        cacheIsSystemNotification();
-    }
-
-    /**
      * Marks a content view as freeable, setting it so that future inflations do not reinflate
      * and ensuring that the view is freed when it is safe to remove.
      *
@@ -1584,6 +1574,7 @@
      * Initialize row.
      */
     public void initialize(
+            NotificationEntry entry,
             String appName,
             String notificationKey,
             ExpansionLogger logger,
@@ -1598,6 +1589,7 @@
             StatusBarStateController statusBarStateController,
             PeopleNotificationIdentifier peopleNotificationIdentifier,
             OnUserInteractionCallback onUserInteractionCallback) {
+        mEntry = entry;
         mAppName = appName;
         if (mMenuRow == null) {
             mMenuRow = new NotificationMenuRow(mContext, peopleNotificationIdentifier);
@@ -1616,12 +1608,15 @@
         mMediaManager = notificationMediaManager;
         setOnFeedbackClickListener(onFeedbackClickListener);
         mFalsingManager = falsingManager;
-        mStatusbarStateController = statusBarStateController;
+        mStatusBarStateController = statusBarStateController;
+
         mPeopleNotificationIdentifier = peopleNotificationIdentifier;
         for (NotificationContentView l : mLayouts) {
             l.setPeopleNotificationIdentifier(mPeopleNotificationIdentifier);
         }
         mOnUserInteractionCallback = onUserInteractionCallback;
+
+        cacheIsSystemNotification();
     }
 
     private void initDimens() {
@@ -2134,8 +2129,8 @@
      */
     @Override
     public boolean isSoundEffectsEnabled() {
-        final boolean mute = mStatusbarStateController != null
-                && mStatusbarStateController.isDozing()
+        final boolean mute = mStatusBarStateController != null
+                && mStatusBarStateController.isDozing()
                 && mSecureStateProvider != null &&
                 !mSecureStateProvider.getAsBoolean();
         return !mute && super.isSoundEffectsEnabled();
@@ -2259,10 +2254,7 @@
         }
     }
 
-    /**
-     * @param onKeyguard whether to prevent notification expansion
-     */
-    public void setOnKeyguard(boolean onKeyguard) {
+    void setOnKeyguard(boolean onKeyguard) {
         if (onKeyguard != mOnKeyguard) {
             boolean wasAboveShelf = isAboveShelf();
             final boolean wasExpanded = isExpanded();
@@ -2331,7 +2323,7 @@
     }
 
     private boolean isDozing() {
-        return mStatusbarStateController != null && mStatusbarStateController.isDozing();
+        return mStatusBarStateController != null && mStatusBarStateController.isDozing();
     }
 
     @Override
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
index f8bc2be..ce760cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -18,6 +18,7 @@
 
 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.StatusBarState.KEYGUARD;
 
 import android.view.View;
 import android.view.ViewGroup;
@@ -29,6 +30,7 @@
 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.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.render.NodeController;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -118,9 +120,10 @@
     /**
      * Initialize the controller.
      */
-    public void init() {
+    public void init(NotificationEntry entry) {
         mActivatableNotificationViewController.init();
         mView.initialize(
+                entry,
                 mAppName,
                 mNotificationKey,
                 mExpansionLogger,
@@ -135,7 +138,15 @@
                 mStatusBarStateController,
                 mPeopleNotificationIdentifier,
                 mOnUserInteractionCallback
+
         );
+        mStatusBarStateController.addCallback(new StatusBarStateController.StateListener() {
+            @Override
+            public void onStateChanged(int newState) {
+                mView.setOnKeyguard(newState == KEYGUARD);
+            }
+        });
+        mView.setOnKeyguard(mStatusBarStateController.getState() == KEYGUARD);
         mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
         if (mAllowLongPress) {
             mView.setLongPressListener((v, x, y, item) -> {
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 60074f6..7d418f3 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
@@ -52,7 +52,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.NotificationLifetimeExtender;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
@@ -121,7 +121,7 @@
     private final INotificationManager mNotificationManager;
     private final LauncherApps mLauncherApps;
     private final ShortcutManager mShortcutManager;
-    private final CurrentUserContextTracker mContextTracker;
+    private final UserContextProvider mContextTracker;
     private final Provider<PriorityOnboardingDialogController.Builder> mBuilderProvider;
     private final UiEventLogger mUiEventLogger;
 
@@ -138,7 +138,7 @@
             LauncherApps launcherApps,
             ShortcutManager shortcutManager,
             ChannelEditorDialogController channelEditorDialogController,
-            CurrentUserContextTracker contextTracker,
+            UserContextProvider contextTracker,
             Provider<PriorityOnboardingDialogController.Builder> builderProvider,
             AssistantFeedbackController assistantFeedbackController,
             BubbleController bubbleController,
@@ -484,7 +484,7 @@
                 onSettingsClick,
                 onSnoozeClickListener,
                 iconFactoryLoader,
-                mContextTracker.getCurrentUserContext(),
+                mContextTracker.getUserContext(),
                 mBuilderProvider,
                 mDeviceProvisionedController.isDeviceProvisioned(),
                 mMainHandler,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 11e698b..0302b2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -42,7 +42,7 @@
     private static final float MAX_PULSE_HEIGHT = 100000f;
 
     private final SectionProvider mSectionProvider;
-    private ArrayList<ExpandableView> mDraggedViews = new ArrayList<>();
+    private ArrayList<View> mDraggedViews = new ArrayList<>();
     private int mScrollY;
     private int mAnchorViewIndex;
     private int mAnchorViewY;
@@ -81,17 +81,17 @@
     private boolean mAppearing;
     private float mPulseHeight = MAX_PULSE_HEIGHT;
     private float mDozeAmount = 0.0f;
-    private HeadsUpManager mHeadUpManager;
     private Runnable mOnPulseHeightChangedListener;
     private ExpandableNotificationRow mTrackedHeadsUpRow;
     private float mAppearFraction;
 
+    /** Tracks the state from AlertingNotificationManager#hasNotifications() */
+    private boolean mHasAlertEntries;
+
     public AmbientState(
             Context context,
-            @NonNull SectionProvider sectionProvider,
-            HeadsUpManager headsUpManager) {
+            @NonNull SectionProvider sectionProvider) {
         mSectionProvider = sectionProvider;
-        mHeadUpManager = headsUpManager;
         reload(context);
     }
 
@@ -164,7 +164,7 @@
     }
 
     /** Call when dragging begins. */
-    public void onBeginDrag(ExpandableView view) {
+    public void onBeginDrag(View view) {
         mDraggedViews.add(view);
     }
 
@@ -172,7 +172,7 @@
         mDraggedViews.remove(view);
     }
 
-    public ArrayList<ExpandableView> getDraggedViews() {
+    public ArrayList<View> getDraggedViews() {
         return mDraggedViews;
     }
 
@@ -393,7 +393,7 @@
     }
 
     public boolean hasPulsingNotifications() {
-        return mPulsing && mHeadUpManager != null && mHeadUpManager.hasNotifications();
+        return mPulsing && mHasAlertEntries;
     }
 
     public void setPulsing(boolean hasPulsing) {
@@ -408,10 +408,7 @@
     }
 
     public boolean isPulsing(NotificationEntry entry) {
-        if (!mPulsing || mHeadUpManager == null) {
-            return false;
-        }
-        return mHeadUpManager.isAlerting(entry.getKey());
+        return mPulsing && entry.isAlerting();
     }
 
     public boolean isPanelTracking() {
@@ -568,4 +565,8 @@
     public float getAppearFraction() {
         return mAppearFraction;
     }
+
+    public void setHasAlertEntries(boolean hasAlertEntries) {
+        mHasAlertEntries = hasAlertEntries;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index e061472..3370773 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -44,7 +44,6 @@
 import android.graphics.Color;
 import android.graphics.Outline;
 import android.graphics.Paint;
-import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
@@ -54,7 +53,6 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -94,11 +92,6 @@
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
-import com.android.systemui.SwipeHelper;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem;
-import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.DragDownHelper.DragDownCallback;
@@ -107,7 +100,6 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.NotificationShelfController;
-import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -132,19 +124,14 @@
 import com.android.systemui.statusbar.notification.row.FooterView;
 import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.notification.row.NotificationGuts;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.notification.row.NotificationSnooze;
 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
-import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
 import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.HeadsUpUtil;
@@ -185,13 +172,12 @@
      * gap is drawn between them). In this case we don't want to round their corners.
      */
     private static final int DISTANCE_BETWEEN_ADJACENT_SECTIONS_PX = 1;
-    private OnMenuEventListener mMenuEventListener;
     private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
     private final DynamicPrivacyController mDynamicPrivacyController;
     private final SysuiStatusBarStateController mStatusbarStateController;
 
     private ExpandHelper mExpandHelper;
-    private final NotificationSwipeHelper mSwipeHelper;
+    private NotificationSwipeHelper mSwipeHelper;
     private int mCurrentStackHeight = Integer.MAX_VALUE;
     private final Paint mBackgroundPaint = new Paint();
     private final boolean mShouldDrawNotificationBackground;
@@ -244,7 +230,7 @@
     /**
      * The algorithm which calculates the properties for our children
      */
-    protected final StackScrollAlgorithm mStackScrollAlgorithm;
+    private final StackScrollAlgorithm mStackScrollAlgorithm;
 
     private final AmbientState mAmbientState;
     private NotificationGroupManager mGroupManager;
@@ -346,13 +332,10 @@
     private HashSet<ExpandableView> mClearTransientViewsWhenFinished = new HashSet<>();
     private HashSet<Pair<ExpandableNotificationRow, Boolean>> mHeadsUpChangeAnimations
             = new HashSet<>();
-    private HeadsUpManagerPhone mHeadsUpManager;
     private final NotificationRoundnessManager mRoundnessManager;
     private boolean mTrackingHeadsUp;
-    private ScrimController mScrimController;
     private boolean mForceNoOverlappingRendering;
     private final ArrayList<Pair<ExpandableNotificationRow, Boolean>> mTmpList = new ArrayList<>();
-    private FalsingManager mFalsingManager;
     private boolean mAnimationRunning;
     private ViewTreeObserver.OnPreDrawListener mRunningAnimationUpdater
             = new ViewTreeObserver.OnPreDrawListener() {
@@ -505,7 +488,6 @@
     private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
     private NotificationPanelViewController mNotificationPanelController;
 
-    private final NotificationGutsManager mNotificationGutsManager;
     private final NotificationSectionsManager mSectionsManager;
     private final ForegroundServiceSectionController mFgsSectionController;
     private ForegroundServiceDungeonView mFgsSectionView;
@@ -519,6 +501,9 @@
     private NotificationStackScrollLayoutController mController;
 
     private boolean mKeyguardMediaControllorVisible;
+    private NotificationEntry mTopHeadsUpEntry;
+    private long mNumHeadsUp;
+    private NotificationStackScrollLayoutController.TouchHandler mTouchHandler;
 
     private final ExpandableView.OnHeightChangedListener mOnChildHeightChangedListener =
             new ExpandableView.OnHeightChangedListener() {
@@ -568,9 +553,6 @@
             NotificationRoundnessManager notificationRoundnessManager,
             DynamicPrivacyController dynamicPrivacyController,
             SysuiStatusBarStateController statusbarStateController,
-            HeadsUpManagerPhone headsUpManager,
-            FalsingManager falsingManager,
-            NotificationGutsManager notificationGutsManager,
             NotificationSectionsManager notificationSectionsManager,
             ForegroundServiceSectionController fgsSectionController,
             ForegroundServiceDismissalFeatureController fgsFeatureController,
@@ -584,11 +566,6 @@
         Resources res = getResources();
 
         mRoundnessManager = notificationRoundnessManager;
-
-        mNotificationGutsManager = notificationGutsManager;
-        mHeadsUpManager = headsUpManager;
-        mHeadsUpManager.setAnimationStateHandler(this::setHeadsUpGoingAwayAnimationsAllowed);
-        mFalsingManager = falsingManager;
         mFgsSectionController = fgsSectionController;
 
         mSectionsManager = notificationSectionsManager;
@@ -600,7 +577,7 @@
         });
         mSections = mSectionsManager.createSectionsForBuckets();
 
-        mAmbientState = new AmbientState(context, mSectionsManager, mHeadsUpManager);
+        mAmbientState = new AmbientState(context, mSectionsManager);
         mBgColor = context.getColor(R.color.notification_shade_background_color);
         int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
         int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
@@ -609,31 +586,9 @@
         mExpandHelper.setEventSource(this);
         mExpandHelper.setScrollAdapter(mScrollAdapter);
 
-        // TODO: move swipe helper into controller.
-        // The anonymous proxy through to mMenuEventListener is temporary until more can be moved
-        // into the controller.
-        mSwipeHelper = new NotificationSwipeHelper(SwipeHelper.X, mNotificationCallback,
-                getContext(), new OnMenuEventListener() {
-            @Override
-            public void onMenuClicked(View row, int x, int y, MenuItem menu) {
-                mMenuEventListener.onMenuClicked(row, x, y, menu);
-            }
-
-            @Override
-            public void onMenuReset(View row) {
-                mMenuEventListener.onMenuReset(row);
-            }
-
-            @Override
-            public void onMenuShown(View row) {
-                mMenuEventListener.onMenuShown(row);
-            }
-        }, mFalsingManager);
         mStackScrollAlgorithm = createStackScrollAlgorithm(context);
         mShouldDrawNotificationBackground =
                 res.getBoolean(R.bool.config_drawNotificationBackground);
-        mFadeNotificationsOnDismiss =
-                res.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
         setOutlineProvider(mOutlineProvider);
 
         // Blocking helper manager wants to know the expanded state, update as well.
@@ -778,27 +733,6 @@
         return false;
     }
 
-  @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-  public RemoteInputController.Delegate createDelegate() {
-        return new RemoteInputController.Delegate() {
-            public void setRemoteInputActive(NotificationEntry entry,
-                    boolean remoteInputActive) {
-                mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
-                entry.notifyHeightChanged(true /* needsAnimation */);
-                updateFooter();
-            }
-
-            public void lockScrollTo(NotificationEntry entry) {
-                NotificationStackScrollLayout.this.lockScrollTo(entry.getRow());
-            }
-
-            public void requestDisallowLongPressAndDismiss() {
-                requestDisallowLongPress();
-                requestDisallowDismiss();
-            }
-        };
-    }
-
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public NotificationSwipeActionHelper getSwipeActionHelper() {
         return mSwipeHelper;
@@ -984,7 +918,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    private void updateBackgroundDimming() {
+    void updateBackgroundDimming() {
         // No need to update the background color if it's not being drawn.
         if (!mShouldDrawNotificationBackground) {
             return;
@@ -1004,14 +938,16 @@
     }
 
     private void reinitView() {
-        initView(getContext(), mKeyguardBypassEnabledProvider);
+        initView(getContext(), mKeyguardBypassEnabledProvider, mSwipeHelper);
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     void initView(Context context,
-            KeyguardBypassEnabledProvider keyguardBypassEnabledProvider) {
+            KeyguardBypassEnabledProvider keyguardBypassEnabledProvider,
+            NotificationSwipeHelper swipeHelper) {
         mScroller = new OverScroller(getContext());
         mKeyguardBypassEnabledProvider = keyguardBypassEnabledProvider;
+        mSwipeHelper = swipeHelper;
 
         setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
         setClipChildren(false);
@@ -1498,11 +1434,10 @@
      */
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     private int getTopHeadsUpPinnedHeight() {
-        NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
-        if (topEntry == null) {
+        if (mTopHeadsUpEntry == null) {
             return 0;
         }
-        ExpandableNotificationRow row = topEntry.getRow();
+        ExpandableNotificationRow row = mTopHeadsUpEntry.getRow();
         if (row.isChildInGroup()) {
             final NotificationEntry groupSummary =
                     mGroupManager.getGroupSummary(row.getEntry().getSbn());
@@ -1523,7 +1458,7 @@
         int visibleNotifCount = getVisibleNotificationCount();
         if (mEmptyShadeView.getVisibility() == GONE && visibleNotifCount > 0) {
             if (isHeadsUpTransition()
-                    || (mHeadsUpManager.hasPinnedHeadsUp() && !mAmbientState.isDozing())) {
+                    || (mInHeadsUpPinnedMode && !mAmbientState.isDozing())) {
                 if (mShelf.getVisibility() != GONE && visibleNotifCount > 1) {
                     appearPosition += mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
                 }
@@ -1655,7 +1590,7 @@
      * @return the child at the given location.
      */
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
-    private ExpandableView getChildAtPosition(float touchX, float touchY,
+    ExpandableView getChildAtPosition(float touchX, float touchY,
             boolean requireMinHeight, boolean ignoreDecors) {
         // find the view under the pointer, accounting for GONE views
         final int count = getChildCount();
@@ -1681,9 +1616,9 @@
                     ExpandableNotificationRow row = (ExpandableNotificationRow) slidingChild;
                     NotificationEntry entry = row.getEntry();
                     if (!mIsExpanded && row.isHeadsUp() && row.isPinned()
-                            && mHeadsUpManager.getTopEntry().getRow() != row
+                            && mTopHeadsUpEntry.getRow() != row
                             && mGroupManager.getGroupSummary(
-                            mHeadsUpManager.getTopEntry().getSbn())
+                            mTopHeadsUpEntry.getSbn())
                             != entry) {
                         continue;
                     }
@@ -1803,7 +1738,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    private boolean onKeyguard() {
+    boolean onKeyguard() {
         return mStatusBarState == StatusBarState.KEYGUARD;
     }
 
@@ -2311,7 +2246,7 @@
         // In current design, it only use the top HUN to treat all of HUNs
         // although there are more than one HUNs
         int contentHeight = mContentHeight;
-        if (!isExpanded() && mHeadsUpManager.hasPinnedHeadsUp()) {
+        if (!isExpanded() && mInHeadsUpPinnedMode) {
             contentHeight = mHeadsUpInset + getTopHeadsUpPinnedHeight();
         }
         int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight);
@@ -2662,7 +2597,7 @@
                     false /* shiftPulsingWithFirst */);
             minTopPosition = firstVisibleSection.getBounds().top;
         }
-        boolean shiftPulsingWithFirst = mHeadsUpManager.getAllEntries().count() <= 1
+        boolean shiftPulsingWithFirst = mNumHeadsUp <= 1
                 && (mAmbientState.isDozing()
                         || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard));
         for (NotificationSection section : mSections) {
@@ -3537,7 +3472,7 @@
                     // Only animate if we still have pinned heads up, otherwise we just have the
                     // regular collapse animation of the lock screen
                     || (mKeyguardBypassEnabledProvider.getBypassEnabled() && onKeyguard()
-                            && mHeadsUpManager.hasPinnedHeadsUp());
+                            && mInHeadsUpPinnedMode);
             if (performDisappearAnimation && !isHeadsUp) {
                 type = row.wasJustClicked()
                         ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
@@ -3780,58 +3715,16 @@
     }
 
     @Override
-    @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean onTouchEvent(MotionEvent ev) {
-        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
-        boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
-                || ev.getActionMasked() == MotionEvent.ACTION_UP;
-        handleEmptySpaceClick(ev);
-        boolean expandWantsIt = false;
-        boolean swipingInProgress = mSwipingInProgress;
-        if (mIsExpanded && !swipingInProgress && !mOnlyScrollingInThisMotion && guts == null) {
-            if (isCancelOrUp) {
-                mExpandHelper.onlyObserveMovements(false);
-            }
-            boolean wasExpandingBefore = mExpandingNotification;
-            expandWantsIt = mExpandHelper.onTouchEvent(ev);
-            if (mExpandedInThisMotion && !mExpandingNotification && wasExpandingBefore
-                    && !mDisallowScrollingInThisMotion) {
-                dispatchDownEventToScroller(ev);
-            }
-        }
-        boolean scrollerWantsIt = false;
-        if (mIsExpanded && !swipingInProgress && !mExpandingNotification
-                && !mDisallowScrollingInThisMotion) {
-            scrollerWantsIt = onScrollTouch(ev);
-        }
-        boolean horizontalSwipeWantsIt = false;
-        if (!mIsBeingDragged
-                && !mExpandingNotification
-                && !mExpandedInThisMotion
-                && !mOnlyScrollingInThisMotion
-                && !mDisallowDismissInThisMotion) {
-            horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
+        if (mTouchHandler != null && mTouchHandler.onTouchEvent(ev)) {
+            return true;
         }
 
-        // Check if we need to clear any snooze leavebehinds
-        if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
-                && guts.getGutsContent() instanceof NotificationSnooze) {
-            NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
-            if ((ns.isExpanded() && isCancelOrUp)
-                    || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
-                // If the leavebehind is expanded we clear it on the next up event, otherwise we
-                // clear it on the next non-horizontal swipe or expand event.
-                checkSnoozeLeavebehind();
-            }
-        }
-        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
-            mCheckForLeavebehind = true;
-        }
-        return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt || super.onTouchEvent(ev);
+        return super.onTouchEvent(ev);
     }
 
     @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void dispatchDownEventToScroller(MotionEvent ev) {
+    void dispatchDownEventToScroller(MotionEvent ev) {
         MotionEvent downEvent = MotionEvent.obtain(ev);
         downEvent.setAction(MotionEvent.ACTION_DOWN);
         onScrollTouch(downEvent);
@@ -3880,7 +3773,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.INPUT)
-    private boolean onScrollTouch(MotionEvent ev) {
+    boolean onScrollTouch(MotionEvent ev) {
         if (!isScrollingEnabled()) {
             return false;
         }
@@ -3969,7 +3862,7 @@
                         customOverScrollBy((int) scrollAmount, mOwnScrollY,
                                 range, getHeight() / 2);
                         // If we're scrolling, leavebehinds should be dismissed
-                        checkSnoozeLeavebehind();
+                        mController.checkSnoozeLeavebehind();
                     }
                 }
                 break;
@@ -4087,44 +3980,14 @@
     @Override
     @ShadeViewRefactor(RefactorComponent.INPUT)
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        initDownStates(ev);
-        handleEmptySpaceClick(ev);
-
-        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
-        boolean expandWantsIt = false;
-        boolean swipingInProgress = mSwipingInProgress;
-        if (!swipingInProgress && !mOnlyScrollingInThisMotion && guts == null) {
-            expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
+        if (mTouchHandler != null && mTouchHandler.onInterceptTouchEvent(ev)) {
+            return true;
         }
-        boolean scrollWantsIt = false;
-        if (!swipingInProgress && !mExpandingNotification) {
-            scrollWantsIt = onInterceptTouchEventScroll(ev);
-        }
-        boolean swipeWantsIt = false;
-        if (!mIsBeingDragged
-                && !mExpandingNotification
-                && !mExpandedInThisMotion
-                && !mOnlyScrollingInThisMotion
-                && !mDisallowDismissInThisMotion) {
-            swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
-        }
-        // Check if we need to clear any snooze leavebehinds
-        boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
-        if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
-                !expandWantsIt && !scrollWantsIt) {
-            mCheckForLeavebehind = false;
-            mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
-                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-        }
-        if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
-            mCheckForLeavebehind = true;
-        }
-        return swipeWantsIt || scrollWantsIt || expandWantsIt || super.onInterceptTouchEvent(ev);
+        return super.onInterceptTouchEvent(ev);
     }
 
     @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void handleEmptySpaceClick(MotionEvent ev) {
+    void handleEmptySpaceClick(MotionEvent ev) {
         switch (ev.getActionMasked()) {
             case MotionEvent.ACTION_MOVE:
                 final float touchSlop = getTouchSlop(ev);
@@ -4143,7 +4006,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void initDownStates(MotionEvent ev) {
+    void initDownStates(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             mExpandedInThisMotion = false;
             mOnlyScrollingInThisMotion = !mScroller.isFinished();
@@ -4165,7 +4028,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.INPUT)
-    private boolean onInterceptTouchEventScroll(MotionEvent ev) {
+    boolean onInterceptTouchEventScroll(MotionEvent ev) {
         if (!isScrollingEnabled()) {
             return false;
         }
@@ -4361,30 +4224,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.INPUT)
-    public void closeControlsIfOutsideTouch(MotionEvent ev) {
-        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
-        NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
-        View translatingParentView = mSwipeHelper.getTranslatingParentView();
-        View view = null;
-        if (guts != null && !guts.getGutsContent().isLeavebehind()) {
-            // Only close visible guts if they're not a leavebehind.
-            view = guts;
-        } else if (menuRow != null && menuRow.isMenuVisible()
-                && translatingParentView != null) {
-            // Checking menu
-            view = translatingParentView;
-        }
-        if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
-            // Touch was outside visible guts / menu notification, close what's visible
-            mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
-                    false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-            resetExposedMenuView(true /* animate */, true /* force */);
-        }
-    }
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void setSwipingInProgress(boolean swiping) {
+    void setSwipingInProgress(boolean swiping) {
         mSwipingInProgress = swiping;
         if (swiping) {
             requestDisallowInterceptTouchEvent(true);
@@ -4418,39 +4258,22 @@
         return Math.max(mMaxLayoutHeight - mContentHeight, 0);
     }
 
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    void checkSnoozeLeavebehind() {
-        if (mCheckForLeavebehind) {
-            mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
-                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-            mCheckForLeavebehind = false;
-        }
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    void resetCheckSnoozeLeavebehind() {
-        mCheckForLeavebehind = true;
-    }
-
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     void onExpansionStarted() {
         mIsExpansionChanging = true;
         mAmbientState.setExpansionChanging(true);
-        checkSnoozeLeavebehind();
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
     void onExpansionStopped() {
         mIsExpansionChanging = false;
-        resetCheckSnoozeLeavebehind();
         mAmbientState.setExpansionChanging(false);
         if (!mIsExpanded) {
             resetScrollPosition();
             mStatusBar.resetUserExpandedStates();
             clearTemporaryViews();
             clearUserLockedViews();
-            ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
+            ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
             if (draggedViews.size() > 0) {
                 draggedViews.clear();
                 updateContinuousShadowDrawing();
@@ -4994,6 +4817,10 @@
         handleDismissAllClipping();
     }
 
+    boolean getDismissAllInProgress() {
+        return mDismissAllInProgress;
+    }
+
     @ShadeViewRefactor(RefactorComponent.ADAPTER)
     private void handleDismissAllClipping() {
         final int count = getChildCount();
@@ -5224,12 +5051,6 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void setScrimController(ScrimController scrimController) {
-        mScrimController = scrimController;
-        mScrimController.setScrimBehindChangeRunnable(this::updateBackgroundDimming);
-    }
-
-    @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
     public void forceNoOverlappingRendering(boolean force) {
         mForceNoOverlappingRendering = force;
     }
@@ -5507,12 +5328,15 @@
             ExpandableView child = (ExpandableView) getTransientView(i);
             child.dump(fd, pw, args);
         }
-        ArrayList<ExpandableView> draggedViews = mAmbientState.getDraggedViews();
+        ArrayList<View> draggedViews = mAmbientState.getDraggedViews();
         int draggedCount = draggedViews.size();
         pw.println("  Dragged Views: " + draggedCount);
         for (int i = 0; i < draggedCount; i++) {
-            ExpandableView child = (ExpandableView) draggedViews.get(i);
-            child.dump(fd, pw, args);
+            View view = draggedViews.get(i);
+            if (view instanceof ExpandableView) {
+                ExpandableView expandableView = (ExpandableView) view;
+                expandableView.dump(fd, pw, args);
+            }
         }
     }
 
@@ -5765,6 +5589,10 @@
         requestChildrenUpdate();
     }
 
+    public boolean isFullyAwake() {
+        return mAmbientState.isFullyAwake();
+    }
+
     public void wakeUpFromPulse() {
         setPulseHeight(getWakeUpHeight());
         // Let's place the hidden views at the end of the pulsing notification to make sure we have
@@ -5826,25 +5654,77 @@
         mCurrentUserId = userId;
     }
 
-    void onMenuShown(View row) {
-        mSwipeHelper.onMenuShown(row);
+    void addSwipedOutView(View v) {
+        mSwipedOutViews.add(v);
     }
 
-    void onMenuReset(View row) {
-        View translatingParentView = mSwipeHelper.getTranslatingParentView();
-        if (translatingParentView != null && row == translatingParentView) {
-            mSwipeHelper.clearExposedMenuView();
-            mSwipeHelper.clearTranslatingParentView();
-            if (row instanceof ExpandableNotificationRow) {
-                mHeadsUpManager.setMenuShown(
-                        ((ExpandableNotificationRow) row).getEntry(), false);
-
-            }
-        }
+    void addDraggedView(View view) {
+        mAmbientState.onBeginDrag(view);
     }
 
-    void setMenuEventListener(OnMenuEventListener menuEventListener) {
-        mMenuEventListener = menuEventListener;
+    void removeDraggedView(View view) {
+        mAmbientState.onDragFinished(view);
+    }
+
+    void setTopHeadsUpEntry(NotificationEntry topEntry) {
+        mTopHeadsUpEntry = topEntry;
+    }
+
+    void setNumHeadsUp(long numHeadsUp) {
+        mNumHeadsUp = numHeadsUp;
+        mAmbientState.setHasAlertEntries(numHeadsUp > 0);
+    }
+
+    boolean getSwipingInProgress() {
+        return mSwipingInProgress;
+    }
+
+    public boolean getIsExpanded() {
+        return mIsExpanded;
+    }
+
+    boolean getOnlyScrollingInThisMotion() {
+        return mOnlyScrollingInThisMotion;
+    }
+
+    ExpandHelper getExpandHelper() {
+        return mExpandHelper;
+    }
+
+    boolean isExpandingNotification() {
+        return mExpandingNotification;
+    }
+
+    boolean getDisallowScrollingInThisMotion() {
+        return mDisallowScrollingInThisMotion;
+    }
+
+    boolean isBeingDragged() {
+        return mIsBeingDragged;
+    }
+
+    boolean getExpandedInThisMotion() {
+        return mExpandedInThisMotion;
+    }
+
+    boolean getDisallowDismissInThisMotion() {
+        return mDisallowDismissInThisMotion;
+    }
+
+    void setCheckForLeaveBehind(boolean checkForLeaveBehind) {
+        mCheckForLeavebehind = checkForLeaveBehind;
+    }
+
+    void setTouchHandler(NotificationStackScrollLayoutController.TouchHandler touchHandler) {
+        mTouchHandler = touchHandler;
+    }
+
+    boolean isSwipingInProgress() {
+        return mSwipingInProgress;
+    }
+
+    boolean getCheckSnoozeLeaveBehind() {
+        return mCheckForLeavebehind;
     }
 
     /**
@@ -5914,7 +5794,7 @@
         mSectionsManager.updateSectionBoundaries(reason);
     }
 
-    private void updateContinuousBackgroundDrawing() {
+    void updateContinuousBackgroundDrawing() {
         boolean continuousBackground = !mAmbientState.isFullyAwake()
                 && !mAmbientState.getDraggedViews().isEmpty();
         if (continuousBackground != mContinuousBackgroundUpdate) {
@@ -5928,7 +5808,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
-    private void updateContinuousShadowDrawing() {
+    void updateContinuousShadowDrawing() {
         boolean continuousShadowUpdate = mAnimationRunning
                 || !mAmbientState.getDraggedViews().isEmpty();
         if (continuousShadowUpdate != mContinuousShadowUpdate) {
@@ -5942,7 +5822,7 @@
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
-    public void resetExposedMenuView(boolean animate, boolean force) {
+    private void resetExposedMenuView(boolean animate, boolean force) {
         mSwipeHelper.resetExposedMenuView(animate, force);
     }
 
@@ -6202,195 +6082,7 @@
         }
     }
 
-
-
-    @ShadeViewRefactor(RefactorComponent.INPUT)
-    private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
-            new NotificationSwipeHelper.NotificationCallback() {
-        @Override
-        public void onDismiss() {
-            mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
-                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
-                    false /* resetMenu */);
-        }
-
-        @Override
-        public void onSnooze(StatusBarNotification sbn,
-                NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
-            mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
-        }
-
-        @Override
-        public void onSnooze(StatusBarNotification sbn, int hours) {
-            mStatusBar.setNotificationSnoozed(sbn, hours);
-        }
-
-        @Override
-        public boolean shouldDismissQuickly() {
-            return NotificationStackScrollLayout.this.isExpanded() && mAmbientState.isFullyAwake();
-        }
-
-        @Override
-        public void onDragCancelled(View v) {
-            setSwipingInProgress(false);
-            mFalsingManager.onNotificationStopDismissing();
-        }
-
-        /**
-         * Handles cleanup after the given {@code view} has been fully swiped out (including
-         * re-invoking dismiss logic in case the notification has not made its way out yet).
-         */
-        @Override
-        public void onChildDismissed(View view) {
-            if (!(view instanceof ActivatableNotificationView)) {
-                return;
-            }
-            ActivatableNotificationView row = (ActivatableNotificationView) view;
-            if (!row.isDismissed()) {
-                handleChildViewDismissed(view);
-            }
-            ViewGroup transientContainer = row.getTransientContainer();
-            if (transientContainer != null) {
-                transientContainer.removeTransientView(view);
-            }
-        }
-
-        /**
-         * Starts up notification dismiss and tells the notification, if any, to remove itself from
-         * layout.
-         *
-         * @param view view (e.g. notification) to dismiss from the layout
-         */
-
-        public void handleChildViewDismissed(View view) {
-            setSwipingInProgress(false);
-            if (mDismissAllInProgress) {
-                return;
-            }
-
-            boolean isBlockingHelperShown = false;
-
-            mAmbientState.onDragFinished(view);
-            updateContinuousShadowDrawing();
-
-            if (view instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) view;
-                if (row.isHeadsUp()) {
-                    mHeadsUpManager.addSwipedOutNotification(
-                            row.getEntry().getSbn().getKey());
-                }
-                isBlockingHelperShown =
-                        row.performDismissWithBlockingHelper(false /* fromAccessibility */);
-            }
-
-            if (view instanceof PeopleHubView) {
-                mSectionsManager.hidePeopleRow();
-            }
-
-            if (!isBlockingHelperShown) {
-                mSwipedOutViews.add(view);
-            }
-            mFalsingManager.onNotificationDismissed();
-            if (mFalsingManager.shouldEnforceBouncer()) {
-                mStatusBar.executeRunnableDismissingKeyguard(
-                        null,
-                        null /* cancelAction */,
-                        false /* dismissShade */,
-                        true /* afterKeyguardGone */,
-                        false /* deferred */);
-            }
-        }
-
-        @Override
-        public boolean isAntiFalsingNeeded() {
-            return onKeyguard();
-        }
-
-        @Override
-        public View getChildAtPosition(MotionEvent ev) {
-            View child = NotificationStackScrollLayout.this.getChildAtPosition(
-                    ev.getX(),
-                    ev.getY(),
-                    true /* requireMinHeight */,
-                    false /* ignoreDecors */);
-            if (child instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                ExpandableNotificationRow parent = row.getNotificationParent();
-                if (parent != null && parent.areChildrenExpanded()
-                        && (parent.areGutsExposed()
-                        || mSwipeHelper.getExposedMenuView() == parent
-                        || (parent.getAttachedChildren().size() == 1
-                        && parent.getEntry().isClearable()))) {
-                    // In this case the group is expanded and showing the menu for the
-                    // group, further interaction should apply to the group, not any
-                    // child notifications so we use the parent of the child. We also do the same
-                    // if we only have a single child.
-                    child = parent;
-                }
-            }
-            return child;
-        }
-
-        @Override
-        public void onBeginDrag(View v) {
-            mFalsingManager.onNotificationStartDismissing();
-            setSwipingInProgress(true);
-            mAmbientState.onBeginDrag((ExpandableView) v);
-            updateContinuousShadowDrawing();
-            updateContinuousBackgroundDrawing();
-            requestChildrenUpdate();
-        }
-
-        @Override
-        public void onChildSnappedBack(View animView, float targetLeft) {
-            mAmbientState.onDragFinished(animView);
-            updateContinuousShadowDrawing();
-            updateContinuousBackgroundDrawing();
-            if (animView instanceof ExpandableNotificationRow) {
-                ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
-                if (row.isPinned() && !canChildBeDismissed(row)
-                        && row.getEntry().getSbn().getNotification().fullScreenIntent
-                                == null) {
-                    mHeadsUpManager.removeNotification(row.getEntry().getSbn().getKey(),
-                            true /* removeImmediately */);
-                }
-            }
-        }
-
-        @Override
-        public boolean updateSwipeProgress(View animView, boolean dismissable,
-                float swipeProgress) {
-            // Returning true prevents alpha fading.
-            return !mFadeNotificationsOnDismiss;
-        }
-
-        @Override
-        public float getFalsingThresholdFactor() {
-            return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
-        }
-
-        @Override
-        public int getConstrainSwipeStartPosition() {
-            NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
-            if (menuRow != null) {
-                return Math.abs(menuRow.getMenuSnapTarget());
-            }
-            return 0;
-        }
-
-        @Override
-        public boolean canChildBeDismissed(View v) {
-            return NotificationStackScrollLayout.canChildBeDismissed(v);
-        }
-
-        @Override
-        public boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) {
-            //TODO: b/131242807 for why this doesn't do anything with direction
-            return canChildBeDismissed(v);
-        }
-    };
-
-    private static boolean canChildBeDismissed(View v) {
+    static boolean canChildBeDismissed(View v) {
         if (v instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) v;
             if (row.isBlockingHelperShowingAndTranslationFinished()) {
@@ -6488,6 +6180,10 @@
         mKeyguardMediaControllorVisible = keyguardMediaControllorVisible;
     }
 
+    void resetCheckSnoozeLeavebehind() {
+        setCheckForLeaveBehind(true);
+    }
+
     // ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
 
     @ShadeViewRefactor(RefactorComponent.INPUT)
@@ -6540,7 +6236,7 @@
         @Override
         public void onTouchSlopExceeded() {
             cancelLongPress();
-            checkSnoozeLeavebehind();
+            mController.checkSnoozeLeavebehind();
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index ca78b2a..70892e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -18,11 +18,14 @@
 
 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 
+import android.content.res.Resources;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
 import android.util.Log;
 import android.view.Display;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowInsets;
@@ -31,8 +34,14 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.ExpandHelper;
+import com.android.systemui.Gefingerpoken;
+import com.android.systemui.R;
+import com.android.systemui.SwipeHelper;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.media.KeyguardMediaController;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -51,7 +60,9 @@
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.ExpandableView;
+import com.android.systemui.statusbar.notification.row.NotificationGuts;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationSnooze;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
@@ -63,6 +74,7 @@
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
+import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.tuner.TunerService;
 
@@ -89,13 +101,22 @@
     private final ConfigurationController mConfigurationController;
     private final ZenModeController mZenModeController;
     private final MetricsLogger mMetricsLogger;
+    private final FalsingManager mFalsingManager;
+    private final NotificationSectionsManager mNotificationSectionsManager;
+    private final Resources mResources;
+    private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
+    private final ScrimController mScrimController;
     private final KeyguardMediaController mKeyguardMediaController;
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final KeyguardBypassController mKeyguardBypassController;
     private final SysuiColorExtractor mColorExtractor;
     private final NotificationLockscreenUserManager mLockscreenUserManager;
+    // TODO: StatusBar should be encapsulated behind a Controller
+    private final StatusBar mStatusBar;
 
     private NotificationStackScrollLayout mView;
+    private boolean mFadeNotificationsOnDismiss;
+    private NotificationSwipeHelper mSwipeHelper;
 
     private final NotificationListContainerImpl mNotificationListContainer =
             new NotificationListContainerImpl();
@@ -203,7 +224,16 @@
 
         @Override
         public void onMenuReset(View row) {
-            mView.onMenuReset(row);
+            View translatingParentView = mSwipeHelper.getTranslatingParentView();
+            if (translatingParentView != null && row == translatingParentView) {
+                mSwipeHelper.clearExposedMenuView();
+                mSwipeHelper.clearTranslatingParentView();
+                if (row instanceof ExpandableNotificationRow) {
+                    mHeadsUpManager.setMenuShown(
+                            ((ExpandableNotificationRow) row).getEntry(), false);
+
+                }
+            }
         }
 
         @Override
@@ -214,7 +244,7 @@
                         .setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
                         .setType(MetricsEvent.TYPE_ACTION));
                 mHeadsUpManager.setMenuShown(notificationRow.getEntry(), true);
-                mView.onMenuShown(row);
+                mSwipeHelper.onMenuShown(row);
                 mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
                         false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
                         false /* resetMenu */);
@@ -232,12 +262,224 @@
                     }
 
                     // Close the menu row since we went directly to the guts
-                    mView.resetExposedMenuView(false, true);
+                    mSwipeHelper.resetExposedMenuView(false, true);
                 }
             }
         }
     };
 
+    private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
+            new NotificationSwipeHelper.NotificationCallback() {
+
+                @Override
+                public void onDismiss() {
+                    mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
+                            false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+                            false /* resetMenu */);
+                }
+
+                @Override
+                public void onSnooze(StatusBarNotification sbn,
+                        NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
+                    mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
+                }
+
+                @Override
+                public void onSnooze(StatusBarNotification sbn, int hours) {
+                    mStatusBar.setNotificationSnoozed(sbn, hours);
+                }
+
+                @Override
+                public boolean shouldDismissQuickly() {
+                    return mView.isExpanded() && mView.isFullyAwake();
+                }
+
+                @Override
+                public void onDragCancelled(View v) {
+                    mView.setSwipingInProgress(false);
+                    mFalsingManager.onNotificationStopDismissing();
+                }
+
+                /**
+                 * Handles cleanup after the given {@code view} has been fully swiped out (including
+                 * re-invoking dismiss logic in case the notification has not made its way out yet).
+                 */
+                @Override
+                public void onChildDismissed(View view) {
+                    if (!(view instanceof ActivatableNotificationView)) {
+                        return;
+                    }
+                    ActivatableNotificationView row = (ActivatableNotificationView) view;
+                    if (!row.isDismissed()) {
+                        handleChildViewDismissed(view);
+                    }
+                    ViewGroup transientContainer = row.getTransientContainer();
+                    if (transientContainer != null) {
+                        transientContainer.removeTransientView(view);
+                    }
+                }
+
+                /**
+                 * Starts up notification dismiss and tells the notification, if any, to remove
+                 * itself from the layout.
+                 *
+                 * @param view view (e.g. notification) to dismiss from the layout
+                 */
+
+                public void handleChildViewDismissed(View view) {
+                    mView.setSwipingInProgress(false);
+                    if (mView.getDismissAllInProgress()) {
+                        return;
+                    }
+
+                    boolean isBlockingHelperShown = false;
+
+                    mView.removeDraggedView(view);
+                    mView.updateContinuousShadowDrawing();
+
+                    if (view instanceof ExpandableNotificationRow) {
+                        ExpandableNotificationRow row = (ExpandableNotificationRow) view;
+                        if (row.isHeadsUp()) {
+                            mHeadsUpManager.addSwipedOutNotification(
+                                    row.getEntry().getSbn().getKey());
+                        }
+                        isBlockingHelperShown =
+                                row.performDismissWithBlockingHelper(false /* fromAccessibility */);
+                    }
+
+                    if (view instanceof PeopleHubView) {
+                        mNotificationSectionsManager.hidePeopleRow();
+                    }
+
+                    if (!isBlockingHelperShown) {
+                        mView.addSwipedOutView(view);
+                    }
+                    mFalsingManager.onNotificationDismissed();
+                    if (mFalsingManager.shouldEnforceBouncer()) {
+                        mStatusBar.executeRunnableDismissingKeyguard(
+                                null,
+                                null /* cancelAction */,
+                                false /* dismissShade */,
+                                true /* afterKeyguardGone */,
+                                false /* deferred */);
+                    }
+                }
+
+                @Override
+                public boolean isAntiFalsingNeeded() {
+                    return mView.onKeyguard();
+                }
+
+                @Override
+                public View getChildAtPosition(MotionEvent ev) {
+                    View child = mView.getChildAtPosition(
+                            ev.getX(),
+                            ev.getY(),
+                            true /* requireMinHeight */,
+                            false /* ignoreDecors */);
+                    if (child instanceof ExpandableNotificationRow) {
+                        ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+                        ExpandableNotificationRow parent = row.getNotificationParent();
+                        if (parent != null && parent.areChildrenExpanded()
+                                && (parent.areGutsExposed()
+                                || mSwipeHelper.getExposedMenuView() == parent
+                                || (parent.getAttachedChildren().size() == 1
+                                && parent.getEntry().isClearable()))) {
+                            // In this case the group is expanded and showing the menu for the
+                            // group, further interaction should apply to the group, not any
+                            // child notifications so we use the parent of the child. We also do the
+                            // same if we only have a single child.
+                            child = parent;
+                        }
+                    }
+                    return child;
+                }
+
+                @Override
+                public void onBeginDrag(View v) {
+                    mFalsingManager.onNotificationStartDismissing();
+                    mView.setSwipingInProgress(true);
+                    mView.addDraggedView(v);
+                    mView.updateContinuousShadowDrawing();
+                    mView.updateContinuousBackgroundDrawing();
+                    mView.requestChildrenUpdate();
+                }
+
+                @Override
+                public void onChildSnappedBack(View animView, float targetLeft) {
+                    mView.addDraggedView(animView);
+                    mView.updateContinuousShadowDrawing();
+                    mView.updateContinuousBackgroundDrawing();
+                    if (animView instanceof ExpandableNotificationRow) {
+                        ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
+                        if (row.isPinned() && !canChildBeDismissed(row)
+                                && row.getEntry().getSbn().getNotification().fullScreenIntent
+                                == null) {
+                            mHeadsUpManager.removeNotification(row.getEntry().getSbn().getKey(),
+                                    true /* removeImmediately */);
+                        }
+                    }
+                }
+
+                @Override
+                public boolean updateSwipeProgress(View animView, boolean dismissable,
+                        float swipeProgress) {
+                    // Returning true prevents alpha fading.
+                    return !mFadeNotificationsOnDismiss;
+                }
+
+                @Override
+                public float getFalsingThresholdFactor() {
+                    return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+                }
+
+                @Override
+                public int getConstrainSwipeStartPosition() {
+                    NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
+                    if (menuRow != null) {
+                        return Math.abs(menuRow.getMenuSnapTarget());
+                    }
+                    return 0;
+                }
+
+                @Override
+                public boolean canChildBeDismissed(View v) {
+                    return NotificationStackScrollLayout.canChildBeDismissed(v);
+                }
+
+                @Override
+                public boolean canChildBeDismissedInDirection(View v, boolean isRightOrDown) {
+                    //TODO: b/131242807 for why this doesn't do anything with direction
+                    return canChildBeDismissed(v);
+                }
+            };
+
+    private final OnHeadsUpChangedListener mOnHeadsUpChangedListener =
+            new OnHeadsUpChangedListener() {
+        @Override
+        public void onHeadsUpPinnedModeChanged(boolean inPinnedMode) {
+            mView.setInHeadsUpPinnedMode(inPinnedMode);
+        }
+
+        @Override
+        public void onHeadsUpPinned(NotificationEntry entry) {
+
+        }
+
+        @Override
+        public void onHeadsUpUnPinned(NotificationEntry entry) {
+
+        }
+
+        @Override
+        public void onHeadsUpStateChanged(NotificationEntry entry, boolean isHeadsUp) {
+            long numEntries = mHeadsUpManager.getAllEntries().count();
+            NotificationEntry topEntry = mHeadsUpManager.getTopEntry();
+            mView.setNumHeadsUp(numEntries);
+            mView.setTopHeadsUpEntry(topEntry);
+        }
+    };
+
     @Inject
     public NotificationStackScrollLayoutController(
             @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
@@ -253,7 +495,13 @@
             ZenModeController zenModeController,
             SysuiColorExtractor colorExtractor,
             NotificationLockscreenUserManager lockscreenUserManager,
-            MetricsLogger metricsLogger) {
+            MetricsLogger metricsLogger,
+            FalsingManager falsingManager,
+            NotificationSectionsManager notificationSectionsManager,
+            @Main Resources resources,
+            NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
+            StatusBar statusBar,
+            ScrimController scrimController) {
         mAllowLongPress = allowLongPress;
         mNotificationGutsManager = notificationGutsManager;
         mHeadsUpManager = headsUpManager;
@@ -268,20 +516,40 @@
         mColorExtractor = colorExtractor;
         mLockscreenUserManager = lockscreenUserManager;
         mMetricsLogger = metricsLogger;
+        mFalsingManager = falsingManager;
+        mNotificationSectionsManager = notificationSectionsManager;
+        mResources = resources;
+        mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder;
+        mStatusBar = statusBar;
+        mScrimController = scrimController;
     }
 
     public void attach(NotificationStackScrollLayout view) {
         mView = view;
         mView.setController(this);
-        mView.initView(mView.getContext(), mKeyguardBypassController::getBypassEnabled);
+        mView.setTouchHandler(new TouchHandler());
+
+        mSwipeHelper = mNotificationSwipeHelperBuilder
+                .setSwipeDirection(SwipeHelper.X)
+                .setNotificationCallback(mNotificationCallback)
+                .setOnMenuEventListener(mMenuEventListener)
+                .build();
+
+        mView.initView(mView.getContext(), mKeyguardBypassController::getBypassEnabled,
+                mSwipeHelper);
 
         mHeadsUpManager.addListener(mNotificationRoundnessManager); // TODO: why is this here?
+        mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
+        mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed);
         mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener);
 
+        mScrimController.setScrimBehindChangeRunnable(mView::updateBackgroundDimming);
+
         mLockscreenUserManager.addUserChangedListener(mLockscreenUserChangeListener);
         mView.setCurrentUserid(mLockscreenUserManager.getCurrentUserId());
 
-        mView.setMenuEventListener(mMenuEventListener);
+        mFadeNotificationsOnDismiss =  // TODO: this should probably be injected directly
+                mResources.getBoolean(R.bool.config_fadeNotificationsOnDismiss);
 
         mNotificationRoundnessManager.setOnRoundingChangedCallback(mView::invalidate);
         mView.addOnExpandedHeightChangedListener(mNotificationRoundnessManager::setExpanded);
@@ -517,7 +785,12 @@
     }
 
     public void checkSnoozeLeavebehind() {
-        mView.checkSnoozeLeavebehind();
+        if (mView.getCheckSnoozeLeaveBehind()) {
+            mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
+                    false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+                    false /* resetMenu */);
+            mView.setCheckForLeaveBehind(false);
+        }
     }
 
     public void setQsExpanded(boolean expanded) {
@@ -540,10 +813,6 @@
         mView.updateTopPadding(qsHeight, animate);
     }
 
-    public void resetCheckSnoozeLeavebehind() {
-        mView.resetCheckSnoozeLeavebehind();
-    }
-
     public boolean isScrolledToBottom() {
         return mView.isScrolledToBottom();
     }
@@ -594,9 +863,11 @@
 
     public void onExpansionStarted() {
         mView.onExpansionStarted();
+        checkSnoozeLeavebehind();
     }
 
     public void onExpansionStopped() {
+        mView.setCheckForLeaveBehind(false);
         mView.onExpansionStopped();
     }
 
@@ -681,8 +952,31 @@
         return mView.hasActiveClearableNotifications(selection);
     }
 
+    /**
+     * Set the maximum number of notifications that can currently be displayed
+     */
+    public void setMaxDisplayedNotifications(int maxNotifications) {
+        mNotificationListContainer.setMaxDisplayedNotifications(maxNotifications);
+    }
+
     public RemoteInputController.Delegate createDelegate() {
-        return mView.createDelegate();
+        return new RemoteInputController.Delegate() {
+            public void setRemoteInputActive(NotificationEntry entry,
+                    boolean remoteInputActive) {
+                mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
+                entry.notifyHeightChanged(true /* needsAnimation */);
+                updateFooter();
+            }
+
+            public void lockScrollTo(NotificationEntry entry) {
+                mView.lockScrollTo(entry.getRow());
+            }
+
+            public void requestDisallowLongPressAndDismiss() {
+                mView.requestDisallowLongPress();
+                mView.requestDisallowDismiss();
+            }
+        };
     }
 
     public void updateSectionBoundaries(String reason) {
@@ -730,18 +1024,10 @@
         mView.setShelfController(notificationShelfController);
     }
 
-    public void setScrimController(ScrimController scrimController) {
-        mView.setScrimController(scrimController);
-    }
-
     public ExpandableView getFirstChildNotGone() {
         return mView.getFirstChildNotGone();
     }
 
-    public void setInHeadsUpPinnedMode(boolean inPinnedMode) {
-        mView.setInHeadsUpPinnedMode(inPinnedMode);
-    }
-
     public void generateHeadsUpAnimation(NotificationEntry entry, boolean isHeadsUp) {
         mView.generateHeadsUpAnimation(entry, isHeadsUp);
     }
@@ -774,7 +1060,7 @@
         return mView.calculateGapHeight(previousView, child, count);
     }
 
-    public NotificationRoundnessManager getNoticationRoundessManager() {
+    NotificationRoundnessManager getNoticationRoundessManager() {
         return mNotificationRoundnessManager;
     }
 
@@ -782,6 +1068,32 @@
         return mNotificationListContainer;
     }
 
+    public void resetCheckSnoozeLeavebehind() {
+        mView.resetCheckSnoozeLeavebehind();
+    }
+
+    public void closeControlsIfOutsideTouch(MotionEvent ev) {
+        NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+        NotificationMenuRowPlugin menuRow = mSwipeHelper.getCurrentMenuRow();
+        View translatingParentView = mSwipeHelper.getTranslatingParentView();
+        View view = null;
+        if (guts != null && !guts.getGutsContent().isLeavebehind()) {
+            // Only close visible guts if they're not a leavebehind.
+            view = guts;
+        } else if (menuRow != null && menuRow.isMenuVisible()
+                && translatingParentView != null) {
+            // Checking menu
+            view = translatingParentView;
+        }
+        if (view != null && !NotificationSwipeHelper.isTouchInView(ev, view)) {
+            // Touch was outside visible guts / menu notification, close what's visible
+            mNotificationGutsManager.closeAndSaveGuts(false /* removeLeavebehind */,
+                    false /* force */, true /* removeControls */, -1 /* x */, -1 /* y */,
+                    false /* resetMenu */);
+            mSwipeHelper.resetExposedMenuView(true /* animate */, true /* force */);
+        }
+    }
+
     private class NotificationListContainerImpl implements NotificationListContainer {
         @Override
         public void setChildTransferInProgress(boolean childTransferInProgress) {
@@ -856,12 +1168,12 @@
 
         @Override
         public void resetExposedMenuView(boolean animate, boolean force) {
-            mView.resetExposedMenuView(animate, force);
+            mSwipeHelper.resetExposedMenuView(animate, force);
         }
 
         @Override
         public NotificationSwipeActionHelper getSwipeActionHelper() {
-            return mView.getSwipeActionHelper();
+            return mSwipeHelper;
         }
 
         @Override
@@ -920,4 +1232,99 @@
             mView.setWillExpand(willExpand);
         }
     }
+
+    class TouchHandler implements Gefingerpoken {
+        @Override
+        public boolean onInterceptTouchEvent(MotionEvent ev) {
+            mView.initDownStates(ev);
+            mView.handleEmptySpaceClick(ev);
+
+            NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+            boolean expandWantsIt = false;
+            boolean swipingInProgress = mView.isSwipingInProgress();
+            if (!swipingInProgress && !mView.getOnlyScrollingInThisMotion() && guts == null) {
+                expandWantsIt = mView.getExpandHelper().onInterceptTouchEvent(ev);
+            }
+            boolean scrollWantsIt = false;
+            if (!swipingInProgress && !mView.isExpandingNotification()) {
+                scrollWantsIt = mView.onInterceptTouchEventScroll(ev);
+            }
+            boolean swipeWantsIt = false;
+            if (!mView.isBeingDragged()
+                    && !mView.isExpandingNotification()
+                    && !mView.getExpandedInThisMotion()
+                    && !mView.getOnlyScrollingInThisMotion()
+                    && !mView.getDisallowDismissInThisMotion()) {
+                swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
+            }
+            // Check if we need to clear any snooze leavebehinds
+            boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
+            if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
+                    !expandWantsIt && !scrollWantsIt) {
+                mView.setCheckForLeaveBehind(false);
+                mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
+                        false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
+                        false /* resetMenu */);
+            }
+            if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+                mView.setCheckForLeaveBehind(true);
+            }
+            return swipeWantsIt || scrollWantsIt || expandWantsIt;
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent ev) {
+            NotificationGuts guts = mNotificationGutsManager.getExposedGuts();
+            boolean isCancelOrUp = ev.getActionMasked() == MotionEvent.ACTION_CANCEL
+                    || ev.getActionMasked() == MotionEvent.ACTION_UP;
+            mView.handleEmptySpaceClick(ev);
+            boolean expandWantsIt = false;
+            boolean swipingInProgress = mView.getSwipingInProgress();
+            boolean onlyScrollingInThisMotion = mView.getOnlyScrollingInThisMotion();
+            boolean expandingNotification = mView.isExpandingNotification();
+            if (mView.getIsExpanded() && !swipingInProgress && !onlyScrollingInThisMotion
+                    && guts == null) {
+                ExpandHelper expandHelper = mView.getExpandHelper();
+                if (isCancelOrUp) {
+                    expandHelper.onlyObserveMovements(false);
+                }
+                boolean wasExpandingBefore = expandingNotification;
+                expandWantsIt = expandHelper.onTouchEvent(ev);
+                expandingNotification = mView.isExpandingNotification();
+                if (mView.getExpandedInThisMotion() && !expandingNotification && wasExpandingBefore
+                        && !mView.getDisallowScrollingInThisMotion()) {
+                    mView.dispatchDownEventToScroller(ev);
+                }
+            }
+            boolean scrollerWantsIt = false;
+            if (mView.isExpanded() && !swipingInProgress && !expandingNotification
+                    && !mView.getDisallowScrollingInThisMotion()) {
+                scrollerWantsIt = mView.onScrollTouch(ev);
+            }
+            boolean horizontalSwipeWantsIt = false;
+            if (!mView.isBeingDragged()
+                    && !expandingNotification
+                    && !mView.getExpandedInThisMotion()
+                    && !onlyScrollingInThisMotion
+                    && !mView.getDisallowDismissInThisMotion()) {
+                horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
+            }
+
+            // Check if we need to clear any snooze leavebehinds
+            if (guts != null && !NotificationSwipeHelper.isTouchInView(ev, guts)
+                    && guts.getGutsContent() instanceof NotificationSnooze) {
+                NotificationSnooze ns = (NotificationSnooze) guts.getGutsContent();
+                if ((ns.isExpanded() && isCancelOrUp)
+                        || (!horizontalSwipeWantsIt && scrollerWantsIt)) {
+                    // If the leavebehind is expanded we clear it on the next up event, otherwise we
+                    // clear it on the next non-horizontal swipe or expand event.
+                    checkSnoozeLeavebehind();
+                }
+            }
+            if (ev.getActionMasked() == MotionEvent.ACTION_UP) {
+                mView.setCheckForLeaveBehind(true);
+            }
+            return horizontalSwipeWantsIt || scrollerWantsIt || expandWantsIt;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index d3b8a8c..ba01c84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -19,15 +19,17 @@
 
 import android.animation.Animator;
 import android.animation.ValueAnimator;
-import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.service.notification.StatusBarNotification;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.SwipeHelper;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -36,6 +38,8 @@
 
 import java.lang.ref.WeakReference;
 
+import javax.inject.Inject;
+
 class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeActionHelper {
 
     @VisibleForTesting
@@ -58,10 +62,10 @@
     private boolean mPulsing;
 
     NotificationSwipeHelper(
-            int swipeDirection, NotificationCallback callback, Context context,
-            NotificationMenuRowPlugin.OnMenuEventListener menuListener,
-            FalsingManager falsingManager) {
-        super(swipeDirection, callback, context, falsingManager);
+            Resources resources, ViewConfiguration viewConfiguration,
+            FalsingManager falsingManager, int swipeDirection, NotificationCallback callback,
+            NotificationMenuRowPlugin.OnMenuEventListener menuListener) {
+        super(swipeDirection, callback, resources, viewConfiguration, falsingManager);
         mMenuListener = menuListener;
         mCallback = callback;
         mFalsingCheck = () -> resetExposedMenuView(true /* animate */, true /* force */);
@@ -74,7 +78,7 @@
     public void clearTranslatingParentView() { setTranslatingParentView(null); }
 
     @VisibleForTesting
-    protected void setTranslatingParentView(View view) { mTranslatingParentView = view; };
+    protected void setTranslatingParentView(View view) { mTranslatingParentView = view; }
 
     public void setExposedMenuView(View view) {
         mMenuExposedView = view;
@@ -90,7 +94,7 @@
 
     @VisibleForTesting
     void setCurrentMenuRow(NotificationMenuRowPlugin menuRow) {
-        mCurrMenuRowRef = menuRow != null ? new WeakReference(menuRow) : null;
+        mCurrMenuRowRef = menuRow != null ? new WeakReference<>(menuRow) : null;
     }
 
     public NotificationMenuRowPlugin getCurrentMenuRow() {
@@ -223,7 +227,7 @@
                 || (isFastNonDismissGesture && isAbleToShowMenu);
         int menuSnapTarget = menuRow.getMenuSnapTarget();
         boolean isNonFalseMenuRevealingGesture =
-                !isFalseGesture(ev) && isMenuRevealingGestureAwayFromMenu;
+                !isFalseGesture() && isMenuRevealingGestureAwayFromMenu;
         if ((isNonDismissGestureTowardsMenu || isNonFalseMenuRevealingGesture)
                 && menuSnapTarget != 0) {
             // Menu has not been snapped to previously and this is menu revealing gesture
@@ -470,4 +474,42 @@
 
         void onDismiss();
     }
+
+    static class Builder {
+        private final Resources mResources;
+        private final ViewConfiguration mViewConfiguration;
+        private final FalsingManager mFalsingManager;
+        private int mSwipeDirection;
+        private NotificationCallback mNotificationCallback;
+        private NotificationMenuRowPlugin.OnMenuEventListener mOnMenuEventListener;
+
+        @Inject
+        Builder(@Main Resources resources, ViewConfiguration viewConfiguration,
+                FalsingManager falsingManager) {
+            mResources = resources;
+            mViewConfiguration = viewConfiguration;
+            mFalsingManager = falsingManager;
+        }
+
+        Builder setSwipeDirection(int swipeDirection) {
+            mSwipeDirection = swipeDirection;
+            return this;
+        }
+
+        Builder setNotificationCallback(NotificationCallback notificationCallback) {
+            mNotificationCallback = notificationCallback;
+            return this;
+        }
+
+        Builder setOnMenuEventListener(
+                NotificationMenuRowPlugin.OnMenuEventListener onMenuEventListener) {
+            mOnMenuEventListener = onMenuEventListener;
+            return this;
+        }
+
+        NotificationSwipeHelper build() {
+            return new NotificationSwipeHelper(mResources, mViewConfiguration, mFalsingManager,
+                    mSwipeDirection, mNotificationCallback, mOnMenuEventListener);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 858023d..ba94202 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -27,6 +27,7 @@
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.classifier.Classifier;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
@@ -317,7 +318,9 @@
         // We snap back if the current translation is not far enough
         boolean snapBack = false;
         if (mCallback.needsAntiFalsing()) {
-            snapBack = snapBack || mFalsingManager.isFalseTouch();
+            snapBack = snapBack || mFalsingManager.isFalseTouch(
+                    mTargetedView == mRightIcon
+                            ? Classifier.RIGHT_AFFORDANCE : Classifier.LEFT_AFFORDANCE);
         }
         snapBack = snapBack || isBelowFalsingThreshold();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index a6811c6..78fcd82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -45,6 +45,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
 import com.android.systemui.statusbar.NotificationMediaManager;
 
 import libcore.io.IoUtils;
@@ -52,6 +53,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Objects;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
@@ -68,6 +70,7 @@
     private final WallpaperManager mWallpaperManager;
     private final KeyguardUpdateMonitor mUpdateMonitor;
     private final Handler mH;
+    private final Optional<FaceAuthScreenBrightnessController> mFaceAuthScreenBrightnessController;
 
     private boolean mCached;
     private Bitmap mCache;
@@ -83,12 +86,14 @@
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             DumpManager dumpManager,
             NotificationMediaManager mediaManager,
+            Optional<FaceAuthScreenBrightnessController> faceAuthScreenBrightnessController,
             @Main Handler mainHandler) {
         dumpManager.registerDumpable(getClass().getSimpleName(), this);
         mWallpaperManager = wallpaperManager;
         mCurrentUserId = ActivityManager.getCurrentUser();
         mUpdateMonitor = keyguardUpdateMonitor;
         mMediaManager = mediaManager;
+        mFaceAuthScreenBrightnessController = faceAuthScreenBrightnessController;
         mH = mainHandler;
 
         if (iWallpaperManager != null) {
@@ -128,6 +133,14 @@
             return LoaderResult.success(null);
         }
 
+        Bitmap faceAuthWallpaper = null;
+        if (mFaceAuthScreenBrightnessController.isPresent()) {
+            faceAuthWallpaper = mFaceAuthScreenBrightnessController.get().getFaceAuthWallpaper();
+            if (faceAuthWallpaper != null) {
+                return LoaderResult.success(faceAuthWallpaper);
+            }
+        }
+
         // Prefer the selected user (when specified) over the current user for the FLAG_SET_LOCK
         // wallpaper.
         final int lockWallpaperUserId =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 5974a53..1cd85e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -18,6 +18,8 @@
 
 import static android.view.View.GONE;
 
+import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
 
@@ -31,6 +33,7 @@
 import android.app.StatusBarManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
@@ -66,10 +69,13 @@
 import com.android.keyguard.KeyguardStatusView;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.dagger.KeyguardStatusViewComponent;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.classifier.Classifier;
 import com.android.systemui.dagger.qualifiers.DisplayId;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
@@ -85,7 +91,6 @@
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
-import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.NotificationShelfController;
 import com.android.systemui.statusbar.PulseExpansionHandler;
 import com.android.systemui.statusbar.RemoteInputController;
@@ -127,7 +132,6 @@
 import java.util.function.Function;
 
 import javax.inject.Inject;
-import javax.inject.Provider;
 
 @StatusBarComponent.StatusBarScope
 public class NotificationPanelViewController extends PanelViewController {
@@ -231,7 +235,7 @@
                         BiometricSourceType biometricSourceType) {
                     boolean
                             keyguardOrShadeLocked =
-                            mBarState == StatusBarState.KEYGUARD
+                            mBarState == KEYGUARD
                                     || mBarState == StatusBarState.SHADE_LOCKED;
                     if (!running && mFirstBypassAttempt && keyguardOrShadeLocked && !mDozing
                             && !mDelayShowingKeyguardStatusBar
@@ -258,7 +262,12 @@
     private final ConversationNotificationManager mConversationNotificationManager;
     private final MediaHierarchyManager mMediaHierarchyManager;
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
-    private final Provider<KeyguardClockSwitchController> mKeyguardClockSwitchControllerProvider;
+    private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
+    // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
+    // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
+    private final int mMaxKeyguardNotifications;
+    // Current max allowed keyguard notifications determined by measuring the panel
+    private int mMaxAllowedKeyguardNotifications;
 
     private KeyguardAffordanceHelper mAffordanceHelper;
     private KeyguardUserSwitcher mKeyguardUserSwitcher;
@@ -482,6 +491,7 @@
 
     @Inject
     public NotificationPanelViewController(NotificationPanelView view,
+            @Main Resources resources,
             InjectionInflationController injectionInflationController,
             NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler,
             DynamicPrivacyController dynamicPrivacyController,
@@ -503,9 +513,9 @@
             MediaHierarchyManager mediaHierarchyManager,
             BiometricUnlockController biometricUnlockController,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
-            Provider<KeyguardClockSwitchController> keyguardClockSwitchControllerProvider,
             NotificationStackScrollLayoutController notificationStackScrollLayoutController,
-            NotificationIconAreaController notificationIconAreaController) {
+            NotificationIconAreaController notificationIconAreaController,
+            KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) {
         super(view, falsingManager, dozeLog, keyguardStateController,
                 (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
                 latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager);
@@ -517,9 +527,9 @@
         mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
         mMediaHierarchyManager = mediaHierarchyManager;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
-        mKeyguardClockSwitchControllerProvider = keyguardClockSwitchControllerProvider;
         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
         mNotificationIconAreaController = notificationIconAreaController;
+        mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
         mView.setWillNotDraw(!DEBUG);
         mInjectionInflationController = injectionInflationController;
         mFalsingManager = falsingManager;
@@ -584,6 +594,7 @@
             mView.getOverlay().add(new DebugDrawable());
         }
 
+        mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
         onFinishInflate();
     }
 
@@ -593,8 +604,10 @@
         mKeyguardStatusView = mView.findViewById(R.id.keyguard_status_view);
 
         KeyguardClockSwitchController keyguardClockSwitchController =
-                mKeyguardClockSwitchControllerProvider.get();
-        keyguardClockSwitchController.attach(mView.findViewById(R.id.keyguard_clock_container));
+                mKeyguardStatusViewComponentFactory
+                        .build(mKeyguardStatusView)
+                        .getKeyguardClockSwitchController();
+        keyguardClockSwitchController.init();
         mBigClockContainer = mView.findViewById(R.id.big_clock_container);
         keyguardClockSwitchController.setBigClockContainer(mBigClockContainer);
 
@@ -724,8 +737,10 @@
         // Re-associate the clock container with the keyguard clock switch.
         mBigClockContainer.removeAllViews();
         KeyguardClockSwitchController keyguardClockSwitchController =
-                mKeyguardClockSwitchControllerProvider.get();
-        keyguardClockSwitchController.attach(mView.findViewById(R.id.keyguard_clock_container));
+                mKeyguardStatusViewComponentFactory
+                        .build(mKeyguardStatusView)
+                        .getKeyguardClockSwitchController();
+        keyguardClockSwitchController.init();
         keyguardClockSwitchController.setBigClockContainer(mBigClockContainer);
 
         // Update keyguard bottom area
@@ -761,6 +776,20 @@
         mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
     }
 
+    private void updateMaxDisplayedNotifications(boolean recompute) {
+        if (recompute) {
+            mMaxAllowedKeyguardNotifications = Math.max(computeMaxKeyguardNotifications(), 1);
+        }
+
+        if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) {
+            mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(
+                    mMaxAllowedKeyguardNotifications);
+        } else {
+            // no max when not on the keyguard
+            mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1);
+        }
+    }
+
     public void setKeyguardIndicationController(KeyguardIndicationController indicationController) {
         mKeyguardIndicationController = indicationController;
         mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
@@ -821,7 +850,7 @@
         boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
         boolean animateClock = animate || mAnimateNextPositionUpdate;
         int stackScrollerPadding;
-        if (mBarState != StatusBarState.KEYGUARD) {
+        if (mBarState != KEYGUARD) {
             stackScrollerPadding = getUnlockedStackScrollerPadding();
         } else {
             int totalHeight = mView.getHeight();
@@ -865,20 +894,17 @@
     }
 
     /**
-     * @param maximum the maximum to return at most
      * @return the maximum keyguard notifications that can fit on the screen
      */
-    public int computeMaxKeyguardNotifications(int maximum) {
+    private int computeMaxKeyguardNotifications() {
         float minPadding = mClockPositionAlgorithm.getMinStackScrollerPadding();
         int notificationPadding = Math.max(
                 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height));
-        NotificationShelf shelf = mNotificationShelfController.getView();
-        float
-                shelfSize =
-                shelf.getVisibility() == View.GONE ? 0
-                        : shelf.getIntrinsicHeight() + notificationPadding;
-        float
-                availableSpace =
+        float shelfSize =
+                mNotificationShelfController.getVisibility() == View.GONE
+                        ? 0
+                        : mNotificationShelfController.getIntrinsicHeight() + notificationPadding;
+        float availableSpace =
                 mNotificationStackScrollLayoutController.getHeight() - minPadding - shelfSize
                         - Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding)
                         - mKeyguardStatusView.getLogoutButtonHeight();
@@ -908,7 +934,8 @@
             availableSpace -= mNotificationStackScrollLayoutController
                     .calculateGapHeight(previousView, child, count);
             previousView = child;
-            if (availableSpace >= 0 && count < maximum) {
+            if (availableSpace >= 0
+                    && (mMaxKeyguardNotifications == -1 || count < mMaxKeyguardNotifications)) {
                 count++;
             } else if (availableSpace > -shelfSize) {
                 // if we are exactly the last view, then we can show us still!
@@ -1235,7 +1262,7 @@
         float vel = getCurrentQSVelocity();
         final int
                 gesture =
-                mBarState == StatusBarState.KEYGUARD ? MetricsEvent.ACTION_LS_QS
+                mBarState == KEYGUARD ? MetricsEvent.ACTION_LS_QS
                         : MetricsEvent.ACTION_SHADE_QS_PULL;
         mLockscreenGestureLogger.write(gesture,
                 (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()),
@@ -1243,7 +1270,7 @@
     }
 
     private boolean flingExpandsQs(float vel) {
-        if (mFalsingManager.isUnlockingDisabled() || isFalseTouch()) {
+        if (mFalsingManager.isUnlockingDisabled() || isFalseTouch(QUICK_SETTINGS)) {
             return false;
         }
         if (Math.abs(vel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
@@ -1253,12 +1280,12 @@
         }
     }
 
-    private boolean isFalseTouch() {
+    private boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
         if (!mKeyguardAffordanceHelperCallback.needsAntiFalsing()) {
             return false;
         }
         if (mFalsingManager.isClassifierEnabled()) {
-            return mFalsingManager.isFalseTouch();
+            return mFalsingManager.isFalseTouch(interactionType);
         }
         return !mQsTouchAboveFalsingThreshold;
     }
@@ -1292,7 +1319,7 @@
     private boolean handleQsTouch(MotionEvent event) {
         final int action = event.getActionMasked();
         if (action == MotionEvent.ACTION_DOWN && getExpandedFraction() == 1f
-                && mBarState != StatusBarState.KEYGUARD && !mQsExpanded && mQsExpansionEnabled) {
+                && mBarState != KEYGUARD && !mQsExpanded && mQsExpansionEnabled) {
 
             // Down in the empty area while fully expanded - go to QS.
             mQsTracking = true;
@@ -1646,7 +1673,7 @@
                     mKeyguardStateController.getShortenedFadingAwayDuration()).setInterpolator(
                     Interpolators.ALPHA_OUT).withEndAction(
                     mAnimateKeyguardBottomAreaInvisibleEndRunnable).start();
-        } else if (statusBarState == StatusBarState.KEYGUARD
+        } else if (statusBarState == KEYGUARD
                 || statusBarState == StatusBarState.SHADE_LOCKED) {
             mKeyguardBottomArea.setVisibility(View.VISIBLE);
             mKeyguardBottomArea.setAlpha(1f);
@@ -1659,8 +1686,8 @@
             boolean goingToFullShade) {
         mKeyguardStatusView.animate().cancel();
         mKeyguardStatusViewAnimating = false;
-        if ((!keyguardFadingAway && mBarState == StatusBarState.KEYGUARD
-                && statusBarState != StatusBarState.KEYGUARD) || goingToFullShade) {
+        if ((!keyguardFadingAway && mBarState == KEYGUARD
+                && statusBarState != KEYGUARD) || goingToFullShade) {
             mKeyguardStatusViewAnimating = true;
             mKeyguardStatusView.animate().alpha(0f).setStartDelay(0).setDuration(
                     160).setInterpolator(Interpolators.ALPHA_OUT).withEndAction(
@@ -1671,14 +1698,14 @@
                         mKeyguardStateController.getShortenedFadingAwayDuration()).start();
             }
         } else if (mBarState == StatusBarState.SHADE_LOCKED
-                && statusBarState == StatusBarState.KEYGUARD) {
+                && statusBarState == KEYGUARD) {
             mKeyguardStatusView.setVisibility(View.VISIBLE);
             mKeyguardStatusViewAnimating = true;
             mKeyguardStatusView.setAlpha(0f);
             mKeyguardStatusView.animate().alpha(1f).setStartDelay(0).setDuration(
                     320).setInterpolator(Interpolators.ALPHA_IN).withEndAction(
                     mAnimateKeyguardStatusViewVisibleEndRunnable);
-        } else if (statusBarState == StatusBarState.KEYGUARD) {
+        } else if (statusBarState == KEYGUARD) {
             if (keyguardFadingAway) {
                 mKeyguardStatusViewAnimating = true;
                 mKeyguardStatusView.animate().alpha(0).translationYBy(
@@ -1698,7 +1725,7 @@
     private void updateQsState() {
         mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded);
         mNotificationStackScrollLayoutController.setScrollingEnabled(
-                mBarState != StatusBarState.KEYGUARD && (!mQsExpanded
+                mBarState != KEYGUARD && (!mQsExpanded
                         || mQsExpansionFromOverscroll));
         updateEmptyShadeView();
 
@@ -1725,7 +1752,7 @@
         updateQsExpansion();
         requestScrollerTopPaddingUpdate(false /* animate */);
         updateHeaderKeyguardAlpha();
-        if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == StatusBarState.KEYGUARD) {
+        if (mBarState == StatusBarState.SHADE_LOCKED || mBarState == KEYGUARD) {
             updateKeyguardBottomAreaAlpha();
             updateBigClockAlpha();
         }
@@ -1767,7 +1794,7 @@
             // Upon initialisation when we are not layouted yet we don't want to announce that we
             // are fully expanded, hence the != 0.0f check.
             return mResources.getString(R.string.accessibility_desc_quick_settings);
-        } else if (mBarState == StatusBarState.KEYGUARD) {
+        } else if (mBarState == KEYGUARD) {
             return mResources.getString(R.string.accessibility_desc_lock_screen);
         } else {
             return mResources.getString(R.string.accessibility_desc_notification_shade);
@@ -1785,7 +1812,7 @@
             // for a nice motion.
             int maxNotificationPadding = getKeyguardNotificationStaticPadding();
             int maxQsPadding = mQsMaxExpansionHeight + mQsNotificationTopPadding;
-            int max = mBarState == StatusBarState.KEYGUARD ? Math.max(
+            int max = mBarState == KEYGUARD ? Math.max(
                     maxNotificationPadding, maxQsPadding) : maxQsPadding;
             return (int) MathUtils.lerp((float) mQsMinExpansionHeight, (float) max,
                     getExpandedFraction());
@@ -1973,7 +2000,7 @@
     @Override
     protected boolean canCollapsePanelOnTouch() {
         if (!isInSettings()) {
-            return mBarState == StatusBarState.KEYGUARD
+            return mBarState == KEYGUARD
                     || mIsPanelCollapseOnQQS
                     || mNotificationStackScrollLayoutController.isScrolledToBottom();
         } else {
@@ -1983,7 +2010,7 @@
 
     @Override
     protected int getMaxPanelHeight() {
-        if (mKeyguardBypassController.getBypassEnabled() && mBarState == StatusBarState.KEYGUARD) {
+        if (mKeyguardBypassController.getBypassEnabled() && mBarState == KEYGUARD) {
             return getMaxPanelHeightBypass();
         } else {
             return getMaxPanelHeightNonBypass();
@@ -1992,7 +2019,7 @@
 
     private int getMaxPanelHeightNonBypass() {
         int min = mStatusBarMinHeight;
-        if (!(mBarState == StatusBarState.KEYGUARD)
+        if (!(mBarState == KEYGUARD)
                 && mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) {
             int minHeight = (int) (mQsMinExpansionHeight + getOverExpansionAmount());
             min = Math.max(min, minHeight);
@@ -2095,7 +2122,7 @@
         int maxHeight = mNotificationStackScrollLayoutController.getHeight() - emptyBottomMargin;
         maxHeight += mNotificationStackScrollLayoutController.getTopPaddingOverflow();
 
-        if (mBarState == StatusBarState.KEYGUARD) {
+        if (mBarState == KEYGUARD) {
             int
                     minKeyguardPanelBottom =
                     mClockPositionAlgorithm.getExpandedClockPosition()
@@ -2132,7 +2159,7 @@
             maxQsHeight = (int) mQsSizeChangeAnimator.getAnimatedValue();
         }
         float totalHeight = Math.max(maxQsHeight,
-                mBarState == StatusBarState.KEYGUARD ? mClockPositionResult.stackScrollerPadding
+                mBarState == KEYGUARD ? mClockPositionResult.stackScrollerPadding
                         : 0) + notificationHeight
                 + mNotificationStackScrollLayoutController.getTopPaddingOverflow();
         if (totalHeight > mNotificationStackScrollLayoutController.getHeight()) {
@@ -2151,7 +2178,7 @@
                 && !mHeadsUpManager.hasPinnedHeadsUp()) {
             alpha = getFadeoutAlpha();
         }
-        if (mBarState == StatusBarState.KEYGUARD && !mHintAnimationRunning
+        if (mBarState == KEYGUARD && !mHintAnimationRunning
                 && !mKeyguardBypassController.getBypassEnabled()) {
             alpha *= mClockPositionResult.clockAlpha;
         }
@@ -2190,14 +2217,14 @@
      * Hides the header when notifications are colliding with it.
      */
     private void updateHeader() {
-        if (mBarState == StatusBarState.KEYGUARD) {
+        if (mBarState == KEYGUARD) {
             updateHeaderKeyguardAlpha();
         }
         updateQsExpansion();
     }
 
     protected float getHeaderTranslation() {
-        if (mBarState == StatusBarState.KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
+        if (mBarState == KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
             return -mQs.getQsMinExpansionHeight();
         }
         float appearAmount = mNotificationStackScrollLayoutController
@@ -2227,7 +2254,7 @@
      */
     private float getKeyguardContentsAlpha() {
         float alpha;
-        if (mBarState == StatusBarState.KEYGUARD) {
+        if (mBarState == KEYGUARD) {
 
             // When on Keyguard, we hide the header as soon as we expanded close enough to the
             // header
@@ -2376,7 +2403,7 @@
         if (mConflictingQsExpansionGesture || mQsExpandImmediate) {
             return;
         }
-        if (mBarState != StatusBarState.KEYGUARD) {
+        if (mBarState != KEYGUARD) {
             mNotificationStackScrollLayoutController.setOnHeightChangedListener(null);
             if (isPixels) {
                 mNotificationStackScrollLayoutController.setOverScrolledPixels(
@@ -2398,7 +2425,7 @@
             mQsExpandImmediate = true;
             mNotificationStackScrollLayoutController.setShouldShowShelfOnly(true);
         }
-        if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
+        if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
             mAffordanceHelper.animateHideLeftRightIcon();
         }
         mNotificationStackScrollLayoutController.onPanelTrackingStarted();
@@ -2413,7 +2440,7 @@
                     true /* animate */);
         }
         mNotificationStackScrollLayoutController.onPanelTrackingStopped();
-        if (expand && (mBarState == StatusBarState.KEYGUARD
+        if (expand && (mBarState == KEYGUARD
                 || mBarState == StatusBarState.SHADE_LOCKED)) {
             if (!mHintAnimationRunning) {
                 mAffordanceHelper.reset(true);
@@ -2564,7 +2591,7 @@
     @Override
     protected boolean onMiddleClicked() {
         switch (mBarState) {
-            case StatusBarState.KEYGUARD:
+            case KEYGUARD:
                 if (!mDozingOnDown) {
                     if (mKeyguardBypassController.getBypassEnabled()) {
                         mUpdateMonitor.requestFaceAuth();
@@ -2579,7 +2606,7 @@
                 return true;
             case StatusBarState.SHADE_LOCKED:
                 if (!mQsExpanded) {
-                    mStatusBarStateController.setState(StatusBarState.KEYGUARD);
+                    mStatusBarStateController.setState(KEYGUARD);
                 }
                 return true;
             case StatusBarState.SHADE:
@@ -2750,7 +2777,7 @@
     }
 
     private boolean isOnKeyguard() {
-        return mBarState == StatusBarState.KEYGUARD;
+        return mBarState == KEYGUARD;
     }
 
     public void setPanelScrimMinFraction(float minFraction) {
@@ -2925,7 +2952,7 @@
             mBottomAreaShadeAlphaAnimator.cancel();
         }
 
-        if (mBarState == StatusBarState.KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
+        if (mBarState == KEYGUARD || mBarState == StatusBarState.SHADE_LOCKED) {
             updateDozingVisibilities(animate);
         }
 
@@ -2953,7 +2980,7 @@
     public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
         if (mAmbientIndicationBottomPadding != ambientIndicationBottomPadding) {
             mAmbientIndicationBottomPadding = ambientIndicationBottomPadding;
-            mStatusBar.updateKeyguardMaxNotifications();
+            updateMaxDisplayedNotifications(true);
         }
     }
 
@@ -3045,9 +3072,6 @@
         if (mKeyguardStatusBar != null) {
             mKeyguardStatusBar.dump(fd, pw, args);
         }
-        if (mKeyguardStatusView != null) {
-            mKeyguardStatusView.dump(fd, pw, args);
-        }
     }
 
     public boolean hasActiveClearableNotifications() {
@@ -3057,7 +3081,7 @@
     private void updateShowEmptyShadeView() {
         boolean
                 showEmptyShadeView =
-                mBarState != StatusBarState.KEYGUARD && !mEntryManager.hasActiveNotifications();
+                mBarState != KEYGUARD && !mEntryManager.hasActiveNotifications();
         showEmptyShadeView(showEmptyShadeView);
     }
 
@@ -3116,17 +3140,16 @@
     public void initDependencies(
             StatusBar statusBar,
             NotificationGroupManager groupManager,
-            NotificationShelfController notificationShelfController,
-            ScrimController scrimController) {
+            NotificationShelfController notificationShelfController) {
         setStatusBar(statusBar);
         setGroupManager(mGroupManager);
         mNotificationStackScrollLayoutController.setNotificationPanelController(this);
         mNotificationStackScrollLayoutController.setStatusBar(statusBar);
         mNotificationStackScrollLayoutController.setGroupManager(groupManager);
         mNotificationStackScrollLayoutController.setShelfController(notificationShelfController);
-        mNotificationStackScrollLayoutController.setScrimController(scrimController);
         updateShowEmptyShadeView();
         mNotificationShelfController = notificationShelfController;
+        updateMaxDisplayedNotifications(true);
     }
 
     public void showTransientIndication(int id) {
@@ -3506,7 +3529,7 @@
 
         @Override
         public boolean needsAntiFalsing() {
-            return mBarState == StatusBarState.KEYGUARD;
+            return mBarState == KEYGUARD;
         }
     }
 
@@ -3521,7 +3544,6 @@
     private class MyOnHeadsUpChangedListener implements OnHeadsUpChangedListener {
         @Override
         public void onHeadsUpPinnedModeChanged(final boolean inPinnedMode) {
-            mNotificationStackScrollLayoutController.setInHeadsUpPinnedMode(inPinnedMode);
             if (inPinnedMode) {
                 mHeadsUpExistenceChangedRunnable.run();
                 updateNotificationTranslucency();
@@ -3619,14 +3641,15 @@
             boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
             boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
             int oldState = mBarState;
-            boolean keyguardShowing = statusBarState == StatusBarState.KEYGUARD;
+            boolean keyguardShowing = statusBarState == KEYGUARD;
+
             setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade);
             setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade);
 
             mBarState = statusBarState;
             mKeyguardShowing = keyguardShowing;
 
-            if (oldState == StatusBarState.KEYGUARD && (goingToFullShade
+            if (oldState == KEYGUARD && (goingToFullShade
                     || statusBarState == StatusBarState.SHADE_LOCKED)) {
                 animateKeyguardStatusBarOut();
                 long
@@ -3635,7 +3658,7 @@
                                 : mKeyguardStateController.calculateGoingToFullShadeDelay();
                 mQs.animateHeaderSlidingIn(delay);
             } else if (oldState == StatusBarState.SHADE_LOCKED
-                    && statusBarState == StatusBarState.KEYGUARD) {
+                    && statusBarState == KEYGUARD) {
                 animateKeyguardStatusBarIn(StackStateAnimator.ANIMATION_DURATION_STANDARD);
                 mNotificationStackScrollLayoutController.resetScrollPosition();
                 // Only animate header if the header is visible. If not, it will partially
@@ -3657,7 +3680,9 @@
             if (keyguardShowing) {
                 updateDozingVisibilities(false /* animate */);
             }
-            // THe update needs to happen after the headerSlide in above, otherwise the translation
+
+            updateMaxDisplayedNotifications(false);
+            // The update needs to happen after the headerSlide in above, otherwise the translation
             // would reset
             updateQSPulseExpansion();
             maybeAnimateBottomAreaAlpha();
@@ -3713,6 +3738,7 @@
                 int oldTop, int oldRight, int oldBottom) {
             DejankUtils.startDetectingBlockingIpcs("NVP#onLayout");
             super.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom);
+            updateMaxDisplayedNotifications(true);
             setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth());
 
             // Update Clock Pivot
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 3c43a17..9ea402d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -101,6 +101,7 @@
             mCallbacks = Lists.newArrayList();
 
     private final SysuiColorExtractor mColorExtractor;
+    private float mFaceAuthDisplayBrightness = LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
 
     @Inject
     public NotificationShadeWindowControllerImpl(Context context, WindowManager windowManager,
@@ -232,6 +233,12 @@
         mScreenBrightnessDoze = value / 255f;
     }
 
+    @Override
+    public void setFaceAuthDisplayBrightness(float brightness) {
+        mFaceAuthDisplayBrightness = brightness;
+        apply(mCurrentState);
+    }
+
     private void setKeyguardDark(boolean dark) {
         int vis = mNotificationShadeView.getSystemUiVisibility();
         if (dark) {
@@ -436,7 +443,7 @@
         if (state.mForceDozeBrightness) {
             mLpChanged.screenBrightness = mScreenBrightnessDoze;
         } else {
-            mLpChanged.screenBrightness = LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
+            mLpChanged.screenBrightness = mFaceAuthDisplayBrightness;
         }
     }
 
@@ -645,6 +652,7 @@
         pw.println(TAG + ":");
         pw.println("  mKeyguardDisplayMode=" + mKeyguardDisplayMode);
         pw.println(mCurrentState);
+        mNotificationShadeView.getViewRootImpl().dump("  ", fd, pw, args);
     }
 
     @Override
@@ -701,6 +709,7 @@
         boolean mHeadsUpShowing;
         boolean mForceCollapsed;
         boolean mForceDozeBrightness;
+        int mFaceAuthDisplayBrightness;
         boolean mForceUserActivity;
         boolean mLaunchingActivity;
         boolean mBackdropShowing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 53cc267..a3d3c2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -52,6 +52,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.InjectionInflationController;
@@ -83,6 +84,7 @@
     private final NotificationShadeWindowView mView;
     private final ShadeController mShadeController;
     private final NotificationShadeDepthController mDepthController;
+    private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
 
     private GestureDetector mGestureDetector;
     private View mBrightnessMirror;
@@ -130,7 +132,8 @@
             NotificationShadeDepthController depthController,
             NotificationShadeWindowView notificationShadeWindowView,
             NotificationPanelViewController notificationPanelViewController,
-            SuperStatusBarViewFactory statusBarViewFactory) {
+            SuperStatusBarViewFactory statusBarViewFactory,
+            NotificationStackScrollLayoutController notificationStackScrollLayoutController) {
         mInjectionInflationController = injectionInflationController;
         mCoordinator = coordinator;
         mPulseExpansionHandler = pulseExpansionHandler;
@@ -152,6 +155,7 @@
         mNotificationPanelViewController = notificationPanelViewController;
         mDepthController = depthController;
         mStatusBarViewFactory = statusBarViewFactory;
+        mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
 
         // This view is not part of the newly inflated expanded status bar.
         mBrightnessMirror = mView.findViewById(R.id.brightness_mirror);
@@ -245,7 +249,7 @@
                     }
                 }
                 if (isDown) {
-                    mStackScrollLayout.closeControlsIfOutsideTouch(ev);
+                    mNotificationStackScrollLayoutController.closeControlsIfOutsideTouch(ev);
                 }
                 if (mStatusBarStateController.isDozing()) {
                     mService.mDozeScrimController.extendPulse();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index ac329e2..0e72506 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -16,6 +16,10 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
+import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
+import static com.android.systemui.classifier.Classifier.UNLOCK;
+
 import static java.lang.Float.isNaN;
 
 import android.animation.Animator;
@@ -41,6 +45,7 @@
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.classifier.Classifier;
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -397,7 +402,12 @@
                 mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
                 mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_UNLOCK);
             }
-            fling(vel, expand, isFalseTouch(x, y));
+            @Classifier.InteractionType int interactionType = vel > 0
+                    ? QUICK_SETTINGS : (
+                            mKeyguardStateController.canDismissLockScreen()
+                                    ? UNLOCK : BOUNCER_UNLOCK);
+
+            fling(vel, expand, isFalseTouch(x, y, interactionType));
             onTrackingStopped(expand);
             mUpdateFlingOnLayout = expand && mPanelClosedOnDown && !mHasLayoutedSinceDown;
             if (mUpdateFlingOnLayout) {
@@ -492,7 +502,11 @@
             return true;
         }
 
-        if (isFalseTouch(x, y)) {
+        @Classifier.InteractionType int interactionType = vel > 0
+                ? QUICK_SETTINGS : (
+                        mKeyguardStateController.canDismissLockScreen() ? UNLOCK : BOUNCER_UNLOCK);
+
+        if (isFalseTouch(x, y, interactionType)) {
             return true;
         }
         if (Math.abs(vectorVel) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
@@ -511,12 +525,13 @@
      * @param y the final y-coordinate when the finger was lifted
      * @return whether this motion should be regarded as a false touch
      */
-    private boolean isFalseTouch(float x, float y) {
+    private boolean isFalseTouch(float x, float y,
+            @Classifier.InteractionType int interactionType) {
         if (!mStatusBar.isFalsingThresholdNeeded()) {
             return false;
         }
         if (mFalsingManager.isClassifierEnabled()) {
-            return mFalsingManager.isFalseTouch();
+            return mFalsingManager.isFalseTouch(interactionType);
         }
         if (!mTouchAboveFalsingThreshold) {
             return true;
@@ -1325,7 +1340,6 @@
         @Override
         public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
                 int oldTop, int oldRight, int oldBottom) {
-            mStatusBar.onPanelLaidOut();
             requestPanelHeightUpdate();
             mHasLayoutedSinceDown = true;
             if (mUpdateFlingOnLayout) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 11d0583..e95cf28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -141,6 +141,8 @@
     private ScrimView mScrimBehind;
     private ScrimView mScrimForBubble;
 
+    private Runnable mScrimBehindChangeRunnable;
+
     private final KeyguardStateController mKeyguardStateController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final DozeParameters mDozeParameters;
@@ -241,6 +243,11 @@
         mScrimInFront = scrimInFront;
         mScrimForBubble = scrimForBubble;
 
+        if (mScrimBehindChangeRunnable != null) {
+            mScrimBehind.setChangeRunnable(mScrimBehindChangeRunnable);
+            mScrimBehindChangeRunnable = null;
+        }
+
         final ScrimState[] states = ScrimState.values();
         for (int i = 0; i < states.length; i++) {
             states[i].init(mScrimInFront, mScrimBehind, mScrimForBubble, mDozeParameters,
@@ -934,7 +941,13 @@
     }
 
     public void setScrimBehindChangeRunnable(Runnable changeRunnable) {
-        mScrimBehind.setChangeRunnable(changeRunnable);
+        // TODO: remove this. This is necessary because of an order-of-operations limitation.
+        // The fix is to move more of these class into @StatusBarScope
+        if (mScrimBehind == null) {
+            mScrimBehindChangeRunnable = changeRunnable;
+        } else {
+            mScrimBehind.setChangeRunnable(changeRunnable);
+        }
     }
 
     public void setCurrentUser(int currentUser) {
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 6e37f90..5882879 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -177,7 +177,7 @@
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.AutoHideUiElement;
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CommandQueue;
@@ -388,7 +388,7 @@
     private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
     private final Provider<StatusBarComponent.Builder> mStatusBarComponentBuilder;
     private final PluginManager mPluginManager;
-    private final Optional<Divider> mDividerOptional;
+    private final Optional<SplitScreen> mSplitScreenOptional;
     private final StatusBarNotificationActivityStarter.Builder
             mStatusBarNotificationActivityStarterBuilder;
     private final ShadeController mShadeController;
@@ -721,7 +721,7 @@
             Optional<Recents> recentsOptional,
             Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
             PluginManager pluginManager,
-            Optional<Divider> dividerOptional,
+            Optional<SplitScreen> splitScreenOptional,
             LightsOutNotifController lightsOutNotifController,
             StatusBarNotificationActivityStarter.Builder
                     statusBarNotificationActivityStarterBuilder,
@@ -803,7 +803,7 @@
         mRecentsOptional = recentsOptional;
         mStatusBarComponentBuilder = statusBarComponentBuilder;
         mPluginManager = pluginManager;
-        mDividerOptional = dividerOptional;
+        mSplitScreenOptional = splitScreenOptional;
         mStatusBarNotificationActivityStarterBuilder = statusBarNotificationActivityStarterBuilder;
         mShadeController = shadeController;
         mSuperStatusBarViewFactory = superStatusBarViewFactory;
@@ -1160,8 +1160,7 @@
         mNotificationPanelViewController.initDependencies(
                 this,
                 mGroupManager,
-                mNotificationShelfController,
-                mScrimController);
+                mNotificationShelfController);
 
         BackDropView backdrop = mNotificationShadeWindowView.findViewById(R.id.backdrop);
         mMediaManager.setup(backdrop, backdrop.findViewById(R.id.backdrop_front),
@@ -1550,31 +1549,32 @@
         if (!mRecentsOptional.isPresent()) {
             return false;
         }
-        Divider divider = null;
-        if (mDividerOptional.isPresent()) {
-            divider = mDividerOptional.get();
-        }
-        if (divider == null || !divider.isDividerVisible()) {
-            final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(mDisplayId);
-            if (navbarPos == NAV_BAR_POS_INVALID) {
-                return false;
-            }
-            int createMode = navbarPos == NAV_BAR_POS_LEFT
-                    ? SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT
-                    : SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-            return mRecentsOptional.get().splitPrimaryTask(createMode, null, metricsDockAction);
-        } else {
-            if (divider.isMinimized() && !divider.isHomeStackResizable()) {
-                // Undocking from the minimized state is not supported
-                return false;
-            } else {
-                divider.onUndockingTask();
-                if (metricsUndockAction != -1) {
-                    mMetricsLogger.action(metricsUndockAction);
+
+        if (mSplitScreenOptional.isPresent()) {
+            SplitScreen splitScreen = mSplitScreenOptional.get();
+            if (splitScreen.isDividerVisible()) {
+                if (splitScreen.isMinimized()
+                        && !splitScreen.isHomeStackResizable()) {
+                    // Undocking from the minimized state is not supported
+                    return false;
+                } else {
+                    splitScreen.onUndockingTask();
+                    if (metricsUndockAction != -1) {
+                        mMetricsLogger.action(metricsUndockAction);
+                    }
                 }
+                return true;
             }
         }
-        return true;
+
+        final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(mDisplayId);
+        if (navbarPos == NAV_BAR_POS_INVALID) {
+            return false;
+        }
+        int createMode = navbarPos == NAV_BAR_POS_LEFT
+                ? SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT
+                : SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+        return mRecentsOptional.get().splitPrimaryTask(createMode, null, metricsDockAction);
     }
 
     /**
@@ -3916,14 +3916,14 @@
     @Override
     public void appTransitionCancelled(int displayId) {
         if (displayId == mDisplayId) {
-            mDividerOptional.ifPresent(Divider::onAppTransitionFinished);
+            mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.onAppTransitionFinished());
         }
     }
 
     @Override
     public void appTransitionFinished(int displayId) {
         if (displayId == mDisplayId) {
-            mDividerOptional.ifPresent(Divider::onAppTransitionFinished);
+            mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.onAppTransitionFinished());
         }
     }
 
@@ -4221,25 +4221,6 @@
         KeyboardShortcuts.dismiss();
     }
 
-    /**
-     * Called when the notification panel layouts
-     */
-    public void onPanelLaidOut() {
-        updateKeyguardMaxNotifications();
-    }
-
-    public void updateKeyguardMaxNotifications() {
-        if (mState == StatusBarState.KEYGUARD) {
-            // Since the number of notifications is determined based on the height of the view, we
-            // need to update them.
-            int maxBefore = mPresenter.getMaxNotificationsWhileLocked(false /* recompute */);
-            int maxNotifications = mPresenter.getMaxNotificationsWhileLocked(true /* recompute */);
-            if (maxBefore != maxNotifications) {
-                mViewHierarchyManager.updateRowStates();
-            }
-        }
-    }
-
     public void executeActionDismissingKeyguard(Runnable action, boolean afterKeyguardGone) {
         if (!mDeviceProvisionedController.isDeviceProvisioned()) return;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 7ee501c..777bf3f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -50,6 +50,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -68,6 +69,7 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.Optional;
 
 import javax.inject.Inject;
 
@@ -103,6 +105,7 @@
     private final ConfigurationController mConfigurationController;
     private final NavigationModeController mNavigationModeController;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
+    private final Optional<FaceAuthScreenBrightnessController> mFaceAuthScreenBrightnessController;
     private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
         @Override
         public void onFullyShown() {
@@ -212,6 +215,7 @@
             DockManager dockManager,
             NotificationShadeWindowController notificationShadeWindowController,
             KeyguardStateController keyguardStateController,
+            Optional<FaceAuthScreenBrightnessController> faceAuthScreenBrightnessController,
             NotificationMediaManager notificationMediaManager) {
         mContext = context;
         mViewMediatorCallback = callback;
@@ -224,6 +228,7 @@
         mKeyguardUpdateManager = keyguardUpdateMonitor;
         mStatusBarStateController = sysuiStatusBarStateController;
         mDockManager = dockManager;
+        mFaceAuthScreenBrightnessController = faceAuthScreenBrightnessController;
     }
 
     @Override
@@ -248,6 +253,11 @@
         notificationPanelViewController.addExpansionListener(this);
         mBypassController = bypassController;
         mNotificationContainer = notificationContainer;
+        mFaceAuthScreenBrightnessController.ifPresent((it) -> {
+            View overlay = new View(mContext);
+            container.addView(overlay);
+            it.attach(overlay);
+        });
 
         registerListeners();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 67adaaa..024a0b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -110,7 +110,6 @@
     private final AboveShelfObserver mAboveShelfObserver;
     private final DozeScrimController mDozeScrimController;
     private final ScrimController mScrimController;
-    private final Context mContext;
     private final KeyguardIndicationController mKeyguardIndicationController;
     private final StatusBar mStatusBar;
     private final ShadeController mShadeController;
@@ -119,7 +118,6 @@
     private final AccessibilityManager mAccessibilityManager;
     private final KeyguardManager mKeyguardManager;
     private final ActivityLaunchAnimator mActivityLaunchAnimator;
-    private final int mMaxAllowedKeyguardNotifications;
     private final IStatusBarService mBarService;
     private final DynamicPrivacyController mDynamicPrivacyController;
     private boolean mReinflateNotificationsOnUserSwitched;
@@ -127,7 +125,6 @@
     private TextView mNotificationPanelDebugText;
 
     protected boolean mVrMode;
-    private int mMaxKeyguardNotifications;
 
     public StatusBarNotificationPresenter(Context context,
             NotificationPanelViewController panel,
@@ -145,7 +142,6 @@
             CommandQueue commandQueue,
             InitController initController,
             NotificationInterruptStateProvider notificationInterruptStateProvider) {
-        mContext = context;
         mKeyguardStateController = keyguardStateController;
         mNotificationPanel = panel;
         mHeadsUpManager = headsUp;
@@ -163,8 +159,6 @@
         mDozeScrimController = dozeScrimController;
         mScrimController = scrimController;
         mKeyguardManager = context.getSystemService(KeyguardManager.class);
-        mMaxAllowedKeyguardNotifications = context.getResources().getInteger(
-                R.integer.keyguard_max_notification_count);
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
 
@@ -397,17 +391,6 @@
     }
 
     @Override
-    public int getMaxNotificationsWhileLocked(boolean recompute) {
-        if (recompute) {
-            mMaxKeyguardNotifications = Math.max(1,
-                    mNotificationPanel.computeMaxKeyguardNotifications(
-                            mMaxAllowedKeyguardNotifications));
-            return mMaxKeyguardNotifications;
-        }
-        return mMaxKeyguardNotifications;
-    }
-
-    @Override
     public void onUpdateRowStates() {
         mNotificationPanel.onUpdateRowStates();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 2768b82..72067d3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -46,7 +46,7 @@
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -180,7 +180,7 @@
             Optional<Recents> recentsOptional,
             Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
             PluginManager pluginManager,
-            Optional<Divider> dividerOptional,
+            Optional<SplitScreen> splitScreenOptional,
             LightsOutNotifController lightsOutNotifController,
             StatusBarNotificationActivityStarter.Builder
                     statusBarNotificationActivityStarterBuilder,
@@ -260,7 +260,7 @@
                 recentsOptional,
                 statusBarComponentBuilder,
                 pluginManager,
-                dividerOptional,
+                splitScreenOptional,
                 lightsOutNotifController,
                 statusBarNotificationActivityStarterBuilder,
                 shadeController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 33b1a4a..f39acf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -39,6 +39,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dump.DumpManager;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -58,6 +59,7 @@
     private static final String TAG = "BluetoothController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
+    private final DumpManager mDumpManager;
     private final LocalBluetoothManager mLocalBluetoothManager;
     private final UserManager mUserManager;
     private final int mCurrentUser;
@@ -77,8 +79,13 @@
     /**
      */
     @Inject
-    public BluetoothControllerImpl(Context context, @Background Looper bgLooper,
-            @Main Looper mainLooper, @Nullable LocalBluetoothManager localBluetoothManager) {
+    public BluetoothControllerImpl(
+            Context context,
+            DumpManager dumpManager,
+            @Background Looper bgLooper,
+            @Main Looper mainLooper,
+            @Nullable LocalBluetoothManager localBluetoothManager) {
+        mDumpManager = dumpManager;
         mLocalBluetoothManager = localBluetoothManager;
         mBgHandler = new Handler(bgLooper);
         mHandler = new H(mainLooper);
@@ -90,6 +97,7 @@
         }
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mCurrentUser = ActivityManager.getCurrentUser();
+        mDumpManager.registerDumpable(TAG, this);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
new file mode 100644
index 0000000..914105f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -0,0 +1,112 @@
+/*
+ * 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.policy.dagger;
+
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.CastControllerImpl;
+import com.android.systemui.statusbar.policy.ExtensionController;
+import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.FlashlightControllerImpl;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.HotspotControllerImpl;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.LocationControllerImpl;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.NextAlarmControllerImpl;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.statusbar.policy.SecurityControllerImpl;
+import com.android.systemui.statusbar.policy.SensorPrivacyController;
+import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
+import com.android.systemui.statusbar.policy.ZenModeController;
+import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
+
+import dagger.Binds;
+import dagger.Module;
+
+
+/** Dagger Module for code in the statusbar.policy package. */
+@Module
+public interface StatusBarPolicyModule {
+    /** */
+    @Binds
+    BluetoothController provideBluetoothController(BluetoothControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    CastController provideCastController(CastControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    ExtensionController provideExtensionController(ExtensionControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    FlashlightController provideFlashlightController(FlashlightControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    KeyguardStateController provideKeyguardMonitor(KeyguardStateControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    HotspotController provideHotspotController(HotspotControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    LocationController provideLocationController(LocationControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    NetworkController provideNetworkController(NetworkControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    NextAlarmController provideNextAlarmController(NextAlarmControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    RotationLockController provideRotationLockController(RotationLockControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    SecurityController provideSecurityController(SecurityControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    SensorPrivacyController provideSensorPrivacyControllerImpl(
+            SensorPrivacyControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    UserInfoController provideUserInfoContrller(UserInfoControllerImpl controllerImpl);
+
+    /** */
+    @Binds
+    ZenModeController provideZenModeController(ZenModeControllerImpl controllerImpl);
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java b/packages/SystemUI/src/com/android/systemui/tuner/dagger/TunerModule.java
similarity index 62%
copy from packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
copy to packages/SystemUI/src/com/android/systemui/tuner/dagger/TunerModule.java
index 114c30e..faf7b84 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/dagger/TunerModule.java
@@ -14,17 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone.dagger;
+package com.android.systemui.tuner.dagger;
 
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerServiceImpl;
 
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
+import dagger.Binds;
+import dagger.Module;
 
-import javax.inject.Qualifier;
-
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface PipMenuActivityClass {
+/** Dagger Module for code in the tuner package. */
+@Module
+public interface TunerModule {
+    /** */
+    @Binds
+    TunerService provideTunerService(TunerServiceImpl controllerImpl);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java
index 37aac11..df741a0 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java
@@ -16,7 +16,9 @@
 
 package com.android.systemui.tv;
 
+import com.android.systemui.dagger.GlobalModule;
 import com.android.systemui.dagger.GlobalRootComponent;
+import com.android.systemui.dagger.WMModule;
 
 import javax.inject.Singleton;
 
@@ -26,7 +28,11 @@
  * Root component for Dagger injection.
  */
 @Singleton
-@Component(modules = {TvSysUIComponentModule.class})
+@Component(modules = {
+        GlobalModule.class,
+        TvSysUIComponentModule.class,
+        WMModule.class
+})
 public interface TvGlobalRootComponent extends GlobalRootComponent {
     /**
      * Component Builder interface. This allows to bind Context instance in the component
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
index 302301d..bef05eb 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java
@@ -17,11 +17,9 @@
 package com.android.systemui.tv;
 
 import com.android.systemui.dagger.DefaultComponentBinder;
-import com.android.systemui.dagger.DependencyBinder;
 import com.android.systemui.dagger.DependencyProvider;
 import com.android.systemui.dagger.SysUIComponent;
 import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.SystemServicesModule;
 import com.android.systemui.dagger.SystemUIBinder;
 import com.android.systemui.dagger.SystemUIModule;
 
@@ -34,8 +32,6 @@
 @Subcomponent(modules = {
         DefaultComponentBinder.class,
         DependencyProvider.class,
-        DependencyBinder.class,
-        SystemServicesModule.class,
         SystemUIBinder.class,
         SystemUIModule.class,
         TvSystemUIModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index ca9cb08..e7c10f1 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -42,7 +42,6 @@
 import com.android.systemui.qs.tileimpl.QSFactoryImpl;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsImplementation;
-import com.android.systemui.stackdivider.DividerModule;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
@@ -76,7 +75,6 @@
  * overridden by the System UI implementation.
  */
 @Module(includes = {
-            DividerModule.class,
             QSModule.class,
             WMShellModule.class
         },
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index d278905..eb8f065 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -24,7 +24,6 @@
 import android.view.View;
 
 import com.android.keyguard.KeyguardMessageArea;
-import com.android.keyguard.KeyguardSliceView;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.qs.QSFooterImpl;
 import com.android.systemui.qs.QSPanel;
@@ -109,11 +108,6 @@
         NotificationStackScrollLayout createNotificationStackScrollLayout();
 
         /**
-         * Creates the KeyguardSliceView.
-         */
-        KeyguardSliceView createKeyguardSliceView();
-
-        /**
          * Creates the KeyguardMessageArea.
          */
         KeyguardMessageArea createKeyguardMessageArea();
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
new file mode 100644
index 0000000..5946af3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/GlobalConcurrencyModule.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+
+import com.android.systemui.dagger.qualifiers.Main;
+
+import java.util.concurrent.Executor;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger Module for classes found within the concurrent package.
+ */
+@Module
+public abstract class GlobalConcurrencyModule {
+
+    /**
+     * Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}.
+     */
+    @Binds
+    public abstract ThreadFactory bindExecutorFactory(ThreadFactoryImpl impl);
+
+     /** Main Looper */
+    @Provides
+    @Main
+    public static  Looper provideMainLooper() {
+        return Looper.getMainLooper();
+    }
+
+    /**
+     * Main Handler.
+     *
+     * Prefer the Main Executor when possible.
+     */
+    @Provides
+    @Main
+    public static Handler provideMainHandler(@Main Looper mainLooper) {
+        return new Handler(mainLooper);
+    }
+
+    /**
+     * Provide a Main-Thread Executor.
+     */
+    @Provides
+    @Main
+    public static Executor provideMainExecutor(Context context) {
+        return context.getMainExecutor();
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
similarity index 85%
rename from packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
rename to packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index 628c808..b9b20c7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.util.concurrency;
 
-import android.content.Context;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -31,7 +30,6 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 
-import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
 
@@ -39,7 +37,7 @@
  * Dagger Module for classes found within the concurrent package.
  */
 @Module
-public abstract class ConcurrencyModule {
+public abstract class SysUIConcurrencyModule {
     /** Background Looper */
     @Provides
     @SysUISingleton
@@ -62,13 +60,6 @@
         return thread.getLooper();
     }
 
-    /** Main Looper */
-    @Provides
-    @Main
-    public static  Looper provideMainLooper() {
-        return Looper.getMainLooper();
-    }
-
     /**
      * Background Handler.
      *
@@ -81,17 +72,6 @@
     }
 
     /**
-     * Main Handler.
-     *
-     * Prefer the Main Executor when possible.
-     */
-    @Provides
-    @Main
-    public static Handler provideMainHandler(@Main Looper mainLooper) {
-        return new Handler(mainLooper);
-    }
-
-    /**
      * Provide a Background-Thread Executor by default.
      */
     @Provides
@@ -121,15 +101,6 @@
     }
 
     /**
-     * Provide a Main-Thread Executor.
-     */
-    @Provides
-    @Main
-    public static Executor provideMainExecutor(Context context) {
-        return context.getMainExecutor();
-    }
-
-    /**
      * Provide a Background-Thread Executor by default.
      */
     @Provides
@@ -199,10 +170,4 @@
     public static Executor provideUiBackgroundExecutor() {
         return Executors.newSingleThreadExecutor();
     }
-
-    /**
-     * Binds {@link ThreadFactoryImpl} to {@link ThreadFactory}.
-     */
-    @Binds
-    public abstract ThreadFactory bindExecutorFactory(ThreadFactoryImpl impl);
 }
diff --git a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
similarity index 60%
copy from libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java
copy to packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
index f1ead3c..cdfa145 100644
--- a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java
+++ b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
@@ -14,25 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell;
+package com.android.systemui.util.dagger;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
+import com.android.systemui.util.RingerModeTracker;
+import com.android.systemui.util.RingerModeTrackerImpl;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import dagger.Binds;
+import dagger.Module;
 
-/**
- * Tests for the shell.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class WindowManagerShellTest {
-
-    WindowManagerShell mShell;
-
-    @Test
-    public void testNothing() {
-        // Do nothing
-    }
+/** Dagger Module for code in the util package. */
+@Module
+public interface UtilModule {
+    /** */
+    @Binds
+    RingerModeTracker provideRingerModeTracker(RingerModeTrackerImpl ringerModeTrackerImpl);
 }
diff --git a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
similarity index 60%
rename from libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java
rename to packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index f1ead3c..1ef4c16 100644
--- a/libs/WindowManager/Shell/tests/src/com/android/wm/shell/WindowManagerShellTest.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -14,25 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.wm.shell;
+package com.android.systemui.volume.dagger;
 
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
+import com.android.systemui.volume.VolumeComponent;
+import com.android.systemui.volume.VolumeDialogComponent;
 
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import dagger.Binds;
+import dagger.Module;
 
-/**
- * Tests for the shell.
- */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class WindowManagerShellTest {
 
-    WindowManagerShell mShell;
-
-    @Test
-    public void testNothing() {
-        // Do nothing
-    }
+/** Dagger Module for code in the volume package. */
+@Module
+public interface VolumeModule {
+    /** */
+    @Binds
+    VolumeComponent provideVolumeComponent(VolumeDialogComponent volumeDialogComponent);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
new file mode 100644
index 0000000..e4ff1b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -0,0 +1,146 @@
+/*
+ * 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.wmshell;
+
+import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+
+import android.app.ActivityManager;
+import android.content.Context;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.SystemUI;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.stackdivider.SplitScreen;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.protolog.ShellProtoLogImpl;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Optional;
+
+import javax.inject.Inject;
+
+/**
+ * Proxy in SysUiScope to delegate events to controllers in WM Shell library.
+ */
+@SysUISingleton
+public final class WMShell extends SystemUI {
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    private final DisplayImeController mDisplayImeController;
+    private final Optional<SplitScreen> mSplitScreenOptional;
+
+    @Inject
+    WMShell(Context context, KeyguardUpdateMonitor keyguardUpdateMonitor,
+            DisplayImeController displayImeController,
+            Optional<SplitScreen> splitScreenOptional) {
+        super(context);
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mDisplayImeController = displayImeController;
+        mSplitScreenOptional = splitScreenOptional;
+    }
+
+    @Override
+    public void start() {
+        // This is to prevent circular init problem by separating registration step out of its
+        // constructor. And make sure the initialization of DisplayImeController won't depend on
+        // specific feature anymore.
+        mDisplayImeController.startMonitorDisplays();
+
+        mSplitScreenOptional.ifPresent(this::initSplitScreen);
+    }
+
+    private void initSplitScreen(SplitScreen splitScreen) {
+        mKeyguardUpdateMonitor.registerCallback(new KeyguardUpdateMonitorCallback() {
+            @Override
+            public void onKeyguardVisibilityChanged(boolean showing) {
+                // Hide the divider when keyguard is showing. Even though keyguard/statusbar is
+                // above everything, it is actually transparent except for notifications, so
+                // we still need to hide any surfaces that are below it.
+                // TODO(b/148906453): Figure out keyguard dismiss animation for divider view.
+                splitScreen.onKeyguardVisibilityChanged(showing);
+            }
+        });
+
+        ActivityManagerWrapper.getInstance().registerTaskStackListener(
+                new TaskStackChangeListener() {
+                    @Override
+                    public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+                            boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
+                        if (!wasVisible || task.configuration.windowConfiguration.getWindowingMode()
+                                != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                                || !splitScreen.isSplitScreenSupported()) {
+                            return;
+                        }
+
+                        if (splitScreen.isMinimized()) {
+                            splitScreen.onUndockingTask();
+                        }
+                    }
+
+                    @Override
+                    public void onActivityForcedResizable(String packageName, int taskId,
+                            int reason) {
+                        splitScreen.onActivityForcedResizable(packageName, taskId, reason);
+                    }
+
+                    @Override
+                    public void onActivityDismissingDockedStack() {
+                        splitScreen.onActivityDismissingSplitScreen();
+                    }
+
+                    @Override
+                    public void onActivityLaunchOnSecondaryDisplayFailed() {
+                        splitScreen.onActivityLaunchOnSecondaryDisplayFailed();
+                    }
+                });
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        // Handle commands if provided
+        for (int i = 0; i < args.length; i++) {
+            switch (args[i]) {
+                case "enable-text-logging": {
+                    String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
+                    startTextLogging(groups);
+                    pw.println("Starting logging on groups: " + Arrays.toString(groups));
+                    return;
+                }
+                case "disable-text-logging": {
+                    String[] groups = Arrays.copyOfRange(args, i + 1, args.length);
+                    stopTextLogging(groups);
+                    pw.println("Stopping logging on groups: " + Arrays.toString(groups));
+                    return;
+                }
+            }
+        }
+
+        // Dump WMShell stuff here if no commands were handled
+    }
+
+    private void startTextLogging(String... groups) {
+        ShellProtoLogImpl.getSingleInstance().startTextLogging(mContext, groups);
+    }
+
+    private void stopTextLogging(String... groups) {
+        ShellProtoLogImpl.getSingleInstance().stopTextLogging(groups);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index 34398cc..0a3172ce 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -24,12 +24,15 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.pip.PipUiEventLogger;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.FloatingContentCoordinator;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.TransactionPool;
 
+import dagger.BindsOptionalOf;
 import dagger.Module;
 import dagger.Provides;
 
@@ -39,7 +42,7 @@
  */
 // TODO(b/162923491): Move most of these dependencies into WMSingleton scope.
 @Module
-public class WMShellBaseModule {
+public abstract class WMShellBaseModule {
     @SysUISingleton
     @Provides
     static TransactionPool provideTransactionPool() {
@@ -58,6 +61,7 @@
     static DeviceConfigProxy provideDeviceConfigProxy() {
         return new DeviceConfigProxy();
     }
+
     @SysUISingleton
     @Provides
     static FloatingContentCoordinator provideFloatingContentCoordinator() {
@@ -77,4 +81,14 @@
         return new SystemWindows(displayController, wmService);
     }
 
+    @SysUISingleton
+    @Provides
+    static ShellTaskOrganizer provideShellTaskOrganizer() {
+        ShellTaskOrganizer organizer = new ShellTaskOrganizer();
+        organizer.registerOrganizer();
+        return organizer;
+    }
+
+    @BindsOptionalOf
+    abstract SplitScreen optionalSplitScreen();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 44bb6ac..6a2ca44 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -16,15 +16,18 @@
 
 package com.android.systemui.wmshell;
 
+import android.content.Context;
 import android.os.Handler;
 import android.view.IWindowManager;
 
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.pip.phone.PipMenuActivity;
-import com.android.systemui.pip.phone.dagger.PipMenuActivityClass;
+import com.android.systemui.stackdivider.SplitScreen;
+import com.android.systemui.stackdivider.SplitScreenController;
+import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.TransactionPool;
 
 import dagger.Module;
@@ -42,15 +45,16 @@
     static DisplayImeController provideDisplayImeController(IWindowManager wmService,
             DisplayController displayController, @Main Handler mainHandler,
             TransactionPool transactionPool) {
-        return new DisplayImeController.Builder(wmService, displayController, mainHandler,
-                transactionPool).build();
+        return new DisplayImeController(wmService, displayController, mainHandler, transactionPool);
     }
 
-    /** TODO(b/150319024): PipMenuActivity will move to a Window */
     @SysUISingleton
-    @PipMenuActivityClass
     @Provides
-    static Class<?> providePipMenuActivityClass() {
-        return PipMenuActivity.class;
+    static SplitScreen provideSplitScreen(Context context,
+            DisplayController displayController, SystemWindows systemWindows,
+            DisplayImeController displayImeController, @Main Handler handler,
+            TransactionPool transactionPool, ShellTaskOrganizer shellTaskOrganizer) {
+        return new SplitScreenController(context, displayController, systemWindows,
+                displayImeController, handler, transactionPool, shellTaskOrganizer);
     }
 }
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index c6331682..db87845 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -80,6 +80,8 @@
                   android:excludeFromRecents="true"
                   />
 
+        <activity android:name="com.android.systemui.screenshot.ScrollViewActivity"
+                  android:exported="false" />
         <provider
             android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
             tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/res/values/strings.xml b/packages/SystemUI/tests/res/values/strings.xml
new file mode 100644
index 0000000..b9f24c8
--- /dev/null
+++ b/packages/SystemUI/tests/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <string name="test_content">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ultrices condimentum ultricies. Sed elementum at massa id sagittis. Nullam dictum massa lorem, nec ornare nunc pharetra vitae. Duis ultrices, felis eu condimentum congue, erat orci efficitur purus, ac rutrum odio lacus sed sapien. Suspendisse erat augue, eleifend eget auctor sagittis, porta eget nibh. Mauris pulvinar urna non justo condimentum, ut vehicula sapien finibus. Aliquam nibh magna, tincidunt ut viverra sed, placerat et turpis. Nam placerat, dui sed tincidunt consectetur, ante velit posuere mauris, tincidunt finibus velit lectus ac tortor. Cras eget lectus feugiat, porttitor velit nec, malesuada massa.</string>
+
+</resources>
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 657e4fb..3aa6ec0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -62,6 +62,8 @@
     private ClockPlugin mClockPlugin;
     @Mock
     ColorExtractor.GradientColors mGradientColors;
+    @Mock
+    KeyguardSliceViewController mKeyguardSliceViewController;
 
     private KeyguardClockSwitchController mController;
 
@@ -69,28 +71,30 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
 
-        mController = new KeyguardClockSwitchController(
-                mStatusBarStateController, mColorExtractor, mClockManager);
-
         when(mView.isAttachedToWindow()).thenReturn(true);
+
+        mController = new KeyguardClockSwitchController(
+                mView, mStatusBarStateController, mColorExtractor, mClockManager,
+                mKeyguardSliceViewController);
+
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
         when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
     }
 
     @Test
-    public void testAttach_viewAlreadyAttached() {
-        mController.attach(mView);
+    public void testInit_viewAlreadyAttached() {
+        mController.init();
 
         verifyAttachment(times(1));
     }
 
     @Test
-    public void testAttach_viewNotYetAttached() {
+    public void testInit_viewNotYetAttached() {
         ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
                 ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
 
         when(mView.isAttachedToWindow()).thenReturn(false);
-        mController.attach(mView);
+        mController.init();
         verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
 
         verifyAttachment(never());
@@ -100,12 +104,17 @@
         verifyAttachment(times(1));
     }
 
+    @Test
+    public void testInitSubControllers() {
+        mController.init();
+        verify(mKeyguardSliceViewController).init();
+    }
 
     @Test
-    public void testAttach_viewDetached() {
+    public void testInit_viewDetached() {
         ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
                 ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
-        mController.attach(mView);
+        mController.init();
         verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());
 
         verifyAttachment(times(1));
@@ -122,7 +131,7 @@
     public void testBigClockPassesStatusBarState() {
         ViewGroup testView = new FrameLayout(mContext);
 
-        mController.attach(mView);
+        mController.init();
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
         mController.setBigClockContainer(testView);
         verify(mView).setBigClockContainer(testView, StatusBarState.SHADE);
@@ -143,7 +152,7 @@
         ArgumentCaptor<ClockManager.ClockChangedListener> listenerArgumentCaptor =
                 ArgumentCaptor.forClass(ClockManager.ClockChangedListener.class);
 
-        mController.attach(mView);
+        mController.init();
         verify(mClockManager).addOnClockChangedListener(listenerArgumentCaptor.capture());
 
         listenerArgumentCaptor.getValue().onClockChanged(mClockPlugin);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
index 446b122..559284a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java
@@ -16,6 +16,7 @@
 
 package com.android.keyguard;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
@@ -28,6 +29,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardDisplayManager.KeyguardPresentation;
+import com.android.keyguard.dagger.KeyguardStatusViewComponent;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.SysuiTestCase;
@@ -51,6 +53,12 @@
     KeyguardSliceView mMockKeyguardSliceView;
     @Mock
     KeyguardStatusView mMockKeyguardStatusView;
+    @Mock
+    private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
+    @Mock
+    private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
+    @Mock
+    private KeyguardClockSwitchController mKeyguardClockSwitchController;
 
     LayoutInflater mLayoutInflater;
 
@@ -62,6 +70,11 @@
         when(mMockKeyguardSliceView.getContext()).thenReturn(mContext);
         when(mMockKeyguardStatusView.getContext()).thenReturn(mContext);
         when(mMockKeyguardStatusView.findViewById(R.id.clock)).thenReturn(mMockKeyguardStatusView);
+        when(mKeyguardStatusViewComponentFactory.build(any(KeyguardStatusView.class)))
+                .thenReturn(mKeyguardStatusViewComponent);
+        when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
+                .thenReturn(mKeyguardClockSwitchController);
+
         allowTestableLooperAsMainThread();
 
         InjectionInflationController inflationController = new InjectionInflationController(
@@ -99,7 +112,8 @@
     @Test
     public void testInflation_doesntCrash() {
         KeyguardPresentation keyguardPresentation = new KeyguardPresentation(mContext,
-                mContext.getDisplayNoVerify(), mLayoutInflater);
+                mContext.getDisplayNoVerify(), mKeyguardStatusViewComponentFactory,
+                mLayoutInflater);
         keyguardPresentation.onCreate(null /*savedInstanceState */);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
new file mode 100644
index 0000000..b7bcaa3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 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.keyguard;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardSliceProvider;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.tuner.TunerService;
+
+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;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper(setAsMainLooper = true)
+public class KeyguardSliceViewControllerTest extends SysuiTestCase {
+    @Mock
+    private KeyguardSliceView mView;;
+    @Mock
+    private KeyguardStatusView mKeyguardStatusView;
+    @Mock
+    private TunerService mTunerService;
+    @Mock
+    private ConfigurationController mConfigurationController;
+    @Mock
+    private ActivityStarter mActivityStarter;
+    private DumpManager mDumpManager = new DumpManager();
+
+    private KeyguardSliceViewController mController;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        when(mView.isAttachedToWindow()).thenReturn(true);
+        when(mView.getContext()).thenReturn(mContext);
+        mController = new KeyguardSliceViewController(
+                mView, mKeyguardStatusView, mActivityStarter, mConfigurationController,
+                mTunerService, mDumpManager);
+        mController.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
+    }
+
+    @Test
+    public void refresh_replacesSliceContentAndNotifiesListener() {
+        mController.refresh();
+        verify(mView).hideSlice();
+    }
+
+    @Test
+    public void onAttachedToWindow_registersListeners() {
+        mController.init();
+        verify(mTunerService).addTunable(any(TunerService.Tunable.class), anyString());
+        verify(mConfigurationController).addCallback(
+                any(ConfigurationController.ConfigurationListener.class));
+    }
+
+    @Test
+    public void onDetachedFromWindow_unregistersListeners() {
+        ArgumentCaptor<View.OnAttachStateChangeListener> attachListenerArgumentCaptor =
+                ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
+
+        mController.init();
+        verify(mView).addOnAttachStateChangeListener(attachListenerArgumentCaptor.capture());
+
+        attachListenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);
+
+        verify(mTunerService).removeTunable(any(TunerService.Tunable.class));
+        verify(mConfigurationController).removeCallback(
+                any(ConfigurationController.ConfigurationListener.class));
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
index 06552b9..1ab08c2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewTest.java
@@ -15,37 +15,27 @@
  */
 package com.android.keyguard;
 
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Color;
 import android.net.Uri;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
-import android.util.AttributeSet;
 import android.view.LayoutInflater;
-import android.view.View;
 
+import androidx.slice.Slice;
 import androidx.slice.SliceProvider;
 import androidx.slice.SliceSpecs;
 import androidx.slice.builders.ListBuilder;
+import androidx.slice.widget.RowContent;
 
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
-import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.tuner.TunerService;
 
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.util.Collections;
@@ -53,46 +43,18 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 
 @SmallTest
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
 @RunWith(AndroidTestingRunner.class)
 public class KeyguardSliceViewTest extends SysuiTestCase {
     private KeyguardSliceView mKeyguardSliceView;
     private Uri mSliceUri;
 
-    @Mock
-    private TunerService mTunerService;
-    @Mock
-    private ConfigurationController mConfigurationController;
-    @Mock
-    private ActivityStarter mActivityStarter;
-    @Mock
-    private Resources mResources;
-
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        allowTestableLooperAsMainThread();
         LayoutInflater layoutInflater = LayoutInflater.from(getContext());
-        layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() {
-
-            @Override
-            public View onCreateView(View parent, String name, Context context,
-                    AttributeSet attrs) {
-                return onCreateView(name, context, attrs);
-            }
-
-            @Override
-            public View onCreateView(String name, Context context, AttributeSet attrs) {
-                if ("com.android.keyguard.KeyguardSliceView".equals(name)) {
-                    return new KeyguardSliceView(getContext(), attrs, mActivityStarter,
-                            mConfigurationController, mTunerService, mResources);
-                }
-                return null;
-            }
-        });
         mKeyguardSliceView = (KeyguardSliceView) layoutInflater
                 .inflate(R.layout.keyguard_status_area, null);
-        mKeyguardSliceView.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
         mSliceUri = Uri.parse(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
         SliceProvider.setSpecs(new HashSet<>(Collections.singletonList(SliceSpecs.LIST)));
     }
@@ -100,9 +62,13 @@
     @Test
     public void showSlice_notifiesListener() {
         ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
+        builder.setHeader(new ListBuilder.HeaderBuilder().setTitle("header title!"));
+        Slice slice = builder.build();
+        RowContent rowContent = new RowContent(slice.getItemArray()[0], 0);
+
         AtomicBoolean notified = new AtomicBoolean();
         mKeyguardSliceView.setContentChangeListener(()-> notified.set(true));
-        mKeyguardSliceView.onChanged(builder.build());
+        mKeyguardSliceView.showSlice(rowContent, Collections.EMPTY_LIST);
         Assert.assertTrue("Listener should be notified about slice changes.",
                 notified.get());
     }
@@ -111,7 +77,7 @@
     public void showSlice_emptySliceNotifiesListener() {
         AtomicBoolean notified = new AtomicBoolean();
         mKeyguardSliceView.setContentChangeListener(()-> notified.set(true));
-        mKeyguardSliceView.onChanged(null);
+        mKeyguardSliceView.showSlice(null, Collections.EMPTY_LIST);
         Assert.assertTrue("Listener should be notified about slice changes.",
                 notified.get());
     }
@@ -119,24 +85,17 @@
     @Test
     public void hasHeader_readsSliceData() {
         ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
-        mKeyguardSliceView.onChanged(builder.build());
+        mKeyguardSliceView.showSlice(null, Collections.EMPTY_LIST);
         Assert.assertFalse("View should not have a header", mKeyguardSliceView.hasHeader());
 
         builder.setHeader(new ListBuilder.HeaderBuilder().setTitle("header title!"));
-        mKeyguardSliceView.onChanged(builder.build());
+        Slice slice = builder.build();
+        RowContent rowContent = new RowContent(slice.getItemArray()[0], 0);
+        mKeyguardSliceView.showSlice(rowContent, Collections.EMPTY_LIST);
         Assert.assertTrue("View should have a header", mKeyguardSliceView.hasHeader());
     }
 
     @Test
-    public void refresh_replacesSliceContentAndNotifiesListener() {
-        AtomicBoolean notified = new AtomicBoolean();
-        mKeyguardSliceView.setContentChangeListener(()-> notified.set(true));
-        mKeyguardSliceView.refresh();
-        Assert.assertTrue("Listener should be notified about slice changes.",
-                notified.get());
-    }
-
-    @Test
     public void getTextColor_whiteTextWhenAOD() {
         // Set text color to red since the default is white and test would always pass
         mKeyguardSliceView.setTextColor(Color.RED);
@@ -147,18 +106,4 @@
         Assert.assertEquals("Should be using AOD text color", Color.WHITE,
                 mKeyguardSliceView.getTextColor());
     }
-
-    @Test
-    public void onAttachedToWindow_registersListeners() {
-        mKeyguardSliceView.onAttachedToWindow();
-        verify(mTunerService).addTunable(eq(mKeyguardSliceView), anyString());
-        verify(mConfigurationController).addCallback(eq(mKeyguardSliceView));
-    }
-
-    @Test
-    public void onDetachedFromWindow_unregistersListeners() {
-        mKeyguardSliceView.onDetachedFromWindow();
-        verify(mTunerService).removeTunable(eq(mKeyguardSliceView));
-        verify(mConfigurationController).removeCallback(eq(mKeyguardSliceView));
-    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
index 0bf1376..0431704 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java
@@ -40,7 +40,8 @@
 public class KeyguardStatusViewTest extends SysuiTestCase {
 
     @Mock
-    KeyguardSliceView mKeyguardSlice;
+    KeyguardSliceViewController mKeyguardSliceViewController;
+
     @Mock
     KeyguardClockSwitch mClockView;
     @InjectMocks
@@ -64,7 +65,7 @@
     @Test
     public void dozeTimeTick_updatesSlice() {
         mKeyguardStatusView.dozeTimeTick();
-        verify(mKeyguardSlice).refresh();
+        verify(mKeyguardSliceViewController).refresh();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
index 861c620..690b9a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
@@ -25,6 +25,7 @@
 import com.android.systemui.util.time.FakeSystemClock
 import org.junit.After
 import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -78,4 +79,15 @@
 
         assertEquals(list, wrapper.readFavorites())
     }
+
+    @Test
+    fun testSaveEmptyOnNonExistingFile() {
+        if (file.exists()) {
+            file.delete()
+        }
+
+        wrapper.storeFavorites(emptyList())
+
+        assertFalse(file.exists())
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index c8e0f49..909acea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -69,7 +69,7 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.GlobalActions;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -125,7 +125,7 @@
     @Mock GlobalActionsPanelPlugin mWalletPlugin;
     @Mock GlobalActionsPanelPlugin.PanelViewController mWalletController;
     @Mock private Handler mHandler;
-    @Mock private CurrentUserContextTracker mCurrentUserContextTracker;
+    @Mock private UserContextProvider mUserContextProvider;
     private ControlsComponent mControlsComponent;
 
     private TestableLooper mTestableLooper;
@@ -137,7 +137,7 @@
         allowTestableLooperAsMainThread();
 
         when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
-        when(mCurrentUserContextTracker.getCurrentUserContext()).thenReturn(mContext);
+        when(mUserContextProvider.getUserContext()).thenReturn(mContext);
         mControlsComponent = new ControlsComponent(
                 true,
                 () -> mControlsController,
@@ -176,7 +176,7 @@
                 mSysUiState,
                 mHandler,
                 mControlsComponent,
-                mCurrentUserContextTracker
+                mUserContextProvider
         );
         mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt
new file mode 100644
index 0000000..58cb032
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/FaceAuthScreenBrightnessControllerTest.kt
@@ -0,0 +1,135 @@
+/*
+ * 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.keyguard
+
+import android.animation.ValueAnimator
+import android.content.res.Resources
+import android.hardware.biometrics.BiometricSourceType
+import android.os.Handler
+import android.provider.Settings.System.SCREEN_BRIGHTNESS_FLOAT
+import android.testing.AndroidTestingRunner
+import android.util.TypedValue
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.Dumpable
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.settings.GlobalSettings
+import com.android.systemui.util.settings.SystemSettings
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+const val INITIAL_BRIGHTNESS = 0.5f
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class FaceAuthScreenBrightnessControllerTest : SysuiTestCase() {
+
+    @Mock
+    lateinit var whiteOverlay: View
+    @Mock
+    lateinit var dumpManager: DumpManager
+    @Mock
+    lateinit var resources: Resources
+    @Mock
+    lateinit var mainHandler: Handler
+    @Mock
+    lateinit var globalSettings: GlobalSettings
+    @Mock
+    lateinit var systemSettings: SystemSettings
+    @Mock
+    lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+    @Mock
+    lateinit var notificationShadeWindowController: NotificationShadeWindowController
+    @Mock
+    lateinit var animator: ValueAnimator
+    @Captor
+    lateinit var keyguardUpdateCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
+    lateinit var faceAuthScreenBrightnessController: FaceAuthScreenBrightnessController
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        faceAuthScreenBrightnessController = object : FaceAuthScreenBrightnessController(
+                notificationShadeWindowController, keyguardUpdateMonitor, resources, globalSettings,
+                systemSettings, mainHandler, dumpManager) {
+            override fun createAnimator(start: Float, end: Float) = animator
+        }
+        `when`(systemSettings.getFloat(eq(SCREEN_BRIGHTNESS_FLOAT))).thenReturn(INITIAL_BRIGHTNESS)
+        faceAuthScreenBrightnessController.attach(whiteOverlay)
+        verify(keyguardUpdateMonitor).registerCallback(capture(keyguardUpdateCallback))
+    }
+
+    @Test
+    fun init_registersDumpManager() {
+        verify(dumpManager).registerDumpable(anyString(), any(Dumpable::class.java))
+    }
+
+    @Test
+    fun init_registersKeyguardCallback() {
+        verify(keyguardUpdateMonitor)
+                .registerCallback(any(KeyguardUpdateMonitorCallback::class.java))
+    }
+
+    @Test
+    fun onBiometricRunningChanged_animatesBrightness() {
+        clearInvocations(whiteOverlay)
+        keyguardUpdateCallback.value
+                .onBiometricRunningStateChanged(true, BiometricSourceType.FACE)
+        verify(whiteOverlay).visibility = eq(View.VISIBLE)
+        verify(animator).start()
+    }
+
+    @Test
+    fun faceAuthWallpaper_whenFaceIsDisabledForUser() {
+        faceAuthScreenBrightnessController.useFaceAuthWallpaper = true
+        faceAuthScreenBrightnessController.faceAuthWallpaper
+        verify(resources, never()).openRawResource(anyInt(), any(TypedValue::class.java))
+    }
+
+    @Test
+    fun faceAuthWallpaper_whenFaceFlagIsDisabled() {
+        faceAuthScreenBrightnessController.useFaceAuthWallpaper = true
+        faceAuthScreenBrightnessController.faceAuthWallpaper
+        verify(resources, never()).openRawResource(anyInt(), any(TypedValue::class.java))
+    }
+
+    @Test
+    fun faceAuthWallpaper_whenFaceIsEnabledForUser() {
+        faceAuthScreenBrightnessController.useFaceAuthWallpaper = true
+        `when`(keyguardUpdateMonitor.isFaceAuthEnabledForUser(anyInt())).thenReturn(true)
+        faceAuthScreenBrightnessController.faceAuthWallpaper
+        verify(resources).openRawResource(anyInt(), any(TypedValue::class.java))
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index c874b1f..f6d6f562 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -37,6 +37,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardDisplayManager;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -46,7 +47,6 @@
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.util.DeviceConfigProxy;
 import com.android.systemui.util.DeviceConfigProxyFake;
-import com.android.systemui.util.InjectionInflationController;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
@@ -72,7 +72,7 @@
     private @Mock PowerManager mPowerManager;
     private @Mock TrustManager mTrustManager;
     private @Mock NavigationModeController mNavigationModeController;
-    private @Mock InjectionInflationController mInjectionInflationController;
+    private @Mock KeyguardDisplayManager mKeyguardDisplayManager;
     private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
 
@@ -91,7 +91,7 @@
                 () -> mStatusBarKeyguardViewManager,
                 mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor,
                 mPowerManager, mTrustManager, mDeviceConfig, mNavigationModeController,
-                mInjectionInflationController);
+                mKeyguardDisplayManager);
         mViewMediator.start();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
index ca8f79d..da1ec98 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaPlayerDataTest.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.mock
@@ -59,6 +60,7 @@
     }
 
     @Test
+    @Ignore("Flaky")
     fun switchPlayersPlaying() {
         val playerIsPlaying1 = mock(MediaControlPanel::class.java)
         whenever(playerIsPlaying1.isPlaying).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
index 7a8e4f7..f385243 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTimeoutListenerTest.kt
@@ -155,6 +155,22 @@
     }
 
     @Test
+    fun testOnMediaDataLoaded_migratesKeys_noTimeoutExtension() {
+        // From not playing
+        mediaTimeoutListener.onMediaDataLoaded(KEY, null, mediaData)
+        clearInvocations(mediaController)
+
+        // Migrate, still not playing
+        val playingState = mock(android.media.session.PlaybackState::class.java)
+        `when`(playingState.state).thenReturn(PlaybackState.STATE_PAUSED)
+        `when`(mediaController.playbackState).thenReturn(playingState)
+        mediaTimeoutListener.onMediaDataLoaded("NEWKEY", KEY, mediaData)
+
+        // Never cancels callback, or schedule another one
+        verify(cancellationRunnable, never()).run()
+    }
+
+    @Test
     fun testOnPlaybackStateChanged_schedulesTimeout_whenPaused() {
         // Assuming we're registered
         testOnMediaDataLoaded_registersPlaybackListener()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
index 2e4d8a7..57dbac5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java
@@ -52,7 +52,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -61,13 +61,13 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
-import java.util.Optional;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Optional;
+
 /** atest NavigationBarControllerTest */
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
@@ -90,13 +90,13 @@
                         mock(AccessibilityManagerWrapper.class),
                         mock(DeviceProvisionedController.class),
                         mock(MetricsLogger.class),
-                        mock(OverviewProxyService.class), 
+                        mock(OverviewProxyService.class),
                         mock(NavigationModeController.class),
                         mock(StatusBarStateController.class),
                         mock(SysUiState.class),
                         mock(BroadcastDispatcher.class),
                         mock(CommandQueue.class),
-                        mock(Divider.class),
+                        Optional.of(mock(SplitScreen.class)),
                         Optional.of(mock(Recents.class)),
                         () -> mock(StatusBar.class),
                         mock(ShadeController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index a643c2d..389c5a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -68,7 +68,7 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
 import com.android.systemui.recents.Recents;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.phone.ShadeController;
@@ -121,7 +121,6 @@
         mDependency.injectMockDependency(KeyguardStateController.class);
         mDependency.injectMockDependency(StatusBarStateController.class);
         mDependency.injectMockDependency(NavigationBarController.class);
-        mDependency.injectMockDependency(Divider.class);
         mOverviewProxyService = mDependency.injectMockDependency(OverviewProxyService.class);
         TestableLooper.get(this).runWithLooper(() -> {
             mHandler = new Handler();
@@ -224,7 +223,7 @@
                 mMockSysUiState,
                 mBroadcastDispatcher,
                 mCommandQueue,
-                mock(Divider.class),
+                Optional.of(mock(SplitScreen.class)),
                 Optional.of(mock(Recents.class)),
                 () -> mock(StatusBar.class),
                 mock(ShadeController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 4c9e141..3e37fde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -33,7 +33,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import org.junit.Before;
@@ -62,7 +62,7 @@
     @Mock
     private Executor mExecutor;
     @Mock
-    private CurrentUserContextTracker mUserContextTracker;
+    private UserContextProvider mUserContextTracker;
     private KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil() {
         public void executeWhenUnlocked(ActivityStarter.OnDismissAction action,
                 boolean requiresShadeOpen) {
@@ -92,7 +92,7 @@
         doNothing().when(mRecordingService).startForeground(anyInt(), any());
         doReturn(mScreenMediaRecorder).when(mRecordingService).getRecorder();
 
-        doReturn(mContext).when(mUserContextTracker).getCurrentUserContext();
+        doReturn(mContext).when(mUserContextTracker).getUserContext();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
new file mode 100644
index 0000000..e7ef64e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot;
+
+import static org.junit.Assert.fail;
+
+import android.content.Intent;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.testing.AndroidTestingRunner;
+import android.util.Log;
+import android.view.Display;
+import android.view.IScrollCaptureClient;
+import android.view.IScrollCaptureController;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests the of internal framework Scroll Capture API from SystemUI.
+ */
+@RunWith(AndroidTestingRunner.class)
+@SmallTest
+public class ScrollCaptureTest extends SysuiTestCase {
+    private static final String TAG = "ScrollCaptureTest";
+
+    /**
+     * Verifies that a request traverses from SystemUI, to WindowManager and to the app process and
+     * is returned without error. Device must be unlocked.
+     */
+    @Test
+    public void testBasicOperation() throws InterruptedException {
+        IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
+
+        // Start an activity to be on top that will be targeted
+        InstrumentationRegistry.getInstrumentation().startActivitySync(
+                new Intent(mContext, ScrollViewActivity.class).addFlags(
+                        Intent.FLAG_ACTIVITY_NEW_TASK));
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        try {
+            wms.requestScrollCapture(Display.DEFAULT_DISPLAY, null, -1,
+                    new IScrollCaptureController.Stub() {
+                        @Override
+                        public void onClientConnected(
+                                IScrollCaptureClient client, Rect scrollBounds,
+                                Point positionInWindow) {
+                            Log.d(TAG,
+                                    "client connected: " + client + "[scrollBounds= " + scrollBounds
+                                            + ", positionInWindow=" + positionInWindow + "]");
+                            latch.countDown();
+                        }
+
+                        @Override
+                        public void onClientUnavailable() {
+                        }
+
+                        @Override
+                        public void onCaptureStarted() {
+                        }
+
+                        @Override
+                        public void onCaptureBufferSent(long frameNumber, Rect capturedArea) {
+                        }
+
+                        @Override
+                        public void onConnectionClosed() {
+                        }
+                    });
+        } catch (RemoteException e) {
+            Log.e(TAG, "request failed", e);
+            fail("caught remote exception " + e);
+        }
+        latch.await(1000, TimeUnit.MILLISECONDS);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
new file mode 100644
index 0000000..bd37259
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollViewActivity.java
@@ -0,0 +1,42 @@
+/*
+ * 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.screenshot;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.TypedValue;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+public class ScrollViewActivity extends Activity {
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        ScrollView scrollView = new ScrollView(this);
+        LinearLayout linearLayout = new LinearLayout(this);
+        linearLayout.setOrientation(LinearLayout.VERTICAL);
+        TextView text = new TextView(this);
+        text.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 40);
+        text.setText(com.android.systemui.R.string.test_content);
+        linearLayout.addView(text);
+        scrollView.addView(linearLayout);
+        setContentView(scrollView);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt
deleted file mode 100644
index 628c06a..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/CurrentUserContextTrackerTest.kt
+++ /dev/null
@@ -1,92 +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 com.android.systemui.settings
-
-import android.content.Context
-import android.content.ContextWrapper
-import android.os.UserHandle
-import android.testing.AndroidTestingRunner
-import android.testing.TestableLooper
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.broadcast.BroadcastDispatcher
-import junit.framework.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.Mockito.mock
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-@TestableLooper.RunWithLooper
-class CurrentUserContextTrackerTest : SysuiTestCase() {
-
-    private lateinit var tracker: CurrentUserContextTracker
-    @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        allowTestableLooperAsMainThread()
-
-        // wrap Context so that tests don't throw for missing package errors
-        val wrapped = object : ContextWrapper(context) {
-            override fun createContextAsUser(user: UserHandle, flags: Int): Context {
-                val mockContext = mock(Context::class.java)
-                `when`(mockContext.user).thenReturn(user)
-                `when`(mockContext.userId).thenReturn(user.identifier)
-                return mockContext
-            }
-        }
-
-        tracker = CurrentUserContextTracker(wrapped, broadcastDispatcher)
-        tracker.initialize()
-    }
-
-    @Test
-    fun testContextExistsAfterInit_noCrash() {
-        tracker.currentUserContext
-    }
-
-    @Test
-    fun testUserContextIsCorrectAfterUserSwitch() {
-        // We always start out with system ui test
-        assertTrue("Starting userId should be 0", tracker.currentUserContext.userId == 0)
-
-        // WHEN user changes
-        tracker.handleUserSwitched(1)
-
-        // THEN user context should have the correct userId
-        assertTrue("User has changed to userId 1, the context should reflect that",
-                tracker.currentUserContext.userId == 1)
-    }
-
-    @Suppress("UNUSED_PARAMETER")
-    @Test(expected = IllegalStateException::class)
-    fun testContextTrackerThrowsExceptionWhenNotInitialized() {
-        // GIVEN an uninitialized CurrentUserContextTracker
-        val userTracker = CurrentUserContextTracker(context, broadcastDispatcher)
-
-        // WHEN client asks for a context
-        val userContext = userTracker.currentUserContext
-
-        // THEN an exception is thrown
-    }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
new file mode 100644
index 0000000..f76b50a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -0,0 +1,292 @@
+/*
+ * 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.settings
+
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.UserInfo
+import android.os.Handler
+import android.os.UserHandle
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.mockito.capture
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import java.util.concurrent.Executor
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class UserTrackerImplTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var context: Context
+    @Mock
+    private lateinit var userManager: UserManager
+    @Mock(stubOnly = true)
+    private lateinit var dumpManager: DumpManager
+    @Mock(stubOnly = true)
+    private lateinit var handler: Handler
+
+    private val executor = Executor(Runnable::run)
+    private lateinit var tracker: UserTrackerImpl
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        `when`(context.userId).thenReturn(UserHandle.USER_SYSTEM)
+        `when`(context.user).thenReturn(UserHandle.SYSTEM)
+        `when`(context.createContextAsUser(any(), anyInt())).thenAnswer { invocation ->
+            val user = invocation.getArgument<UserHandle>(0)
+            `when`(context.user).thenReturn(user)
+            `when`(context.userId).thenReturn(user.identifier)
+            context
+        }
+        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+            val info = UserInfo(invocation.getArgument<Int>(0), "", UserInfo.FLAG_FULL)
+            listOf(info)
+        }
+
+        tracker = UserTrackerImpl(context, userManager, dumpManager, handler)
+    }
+
+    @Test
+    fun testNotInitialized() {
+        assertThat(tracker.initialized).isFalse()
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserIdBeforeInitThrowsException() {
+        tracker.userId
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserHandleBeforeInitThrowsException() {
+        tracker.userHandle
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserContextBeforeInitThrowsException() {
+        tracker.userContext
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserContentResolverBeforeInitThrowsException() {
+        tracker.userContentResolver
+    }
+
+    @Test(expected = IllegalStateException::class)
+    fun testGetUserProfilesBeforeInitThrowsException() {
+        tracker.userProfiles
+    }
+
+    @Test
+    fun testInitialize() {
+        tracker.initialize(0)
+
+        assertThat(tracker.initialized).isTrue()
+    }
+
+    @Test
+    fun testReceiverRegisteredOnInitialize() {
+        tracker.initialize(0)
+
+        val captor = ArgumentCaptor.forClass(IntentFilter::class.java)
+
+        verify(context).registerReceiverForAllUsers(
+                eq(tracker), capture(captor), isNull(), eq(handler))
+    }
+
+    @Test
+    fun testInitialValuesSet() {
+        val testID = 4
+        tracker.initialize(testID)
+
+        verify(userManager).getProfiles(testID)
+
+        assertThat(tracker.userId).isEqualTo(testID)
+        assertThat(tracker.userHandle).isEqualTo(UserHandle.of(testID))
+        assertThat(tracker.userContext.userId).isEqualTo(testID)
+        assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(testID))
+        assertThat(tracker.userProfiles).hasSize(1)
+
+        val info = tracker.userProfiles[0]
+        assertThat(info.id).isEqualTo(testID)
+    }
+
+    @Test
+    fun testUserSwitch() {
+        tracker.initialize(0)
+        val newID = 5
+
+        val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID)
+        tracker.onReceive(context, intent)
+
+        verify(userManager).getProfiles(newID)
+
+        assertThat(tracker.userId).isEqualTo(newID)
+        assertThat(tracker.userHandle).isEqualTo(UserHandle.of(newID))
+        assertThat(tracker.userContext.userId).isEqualTo(newID)
+        assertThat(tracker.userContext.user).isEqualTo(UserHandle.of(newID))
+        assertThat(tracker.userProfiles).hasSize(1)
+
+        val info = tracker.userProfiles[0]
+        assertThat(info.id).isEqualTo(newID)
+    }
+
+    @Test
+    fun testManagedProfileAvailable() {
+        tracker.initialize(0)
+        val profileID = tracker.userId + 10
+
+        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+            val id = invocation.getArgument<Int>(0)
+            val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+            val infoProfile = UserInfo(
+                    id + 10,
+                    "",
+                    "",
+                    UserInfo.FLAG_MANAGED_PROFILE,
+                    UserManager.USER_TYPE_PROFILE_MANAGED
+            )
+            infoProfile.profileGroupId = id
+            listOf(info, infoProfile)
+        }
+
+        val intent = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+        tracker.onReceive(context, intent)
+
+        assertThat(tracker.userProfiles.map { it.id }).containsExactly(tracker.userId, profileID)
+    }
+
+    @Test
+    fun testCallbackNotCalledOnAdd() {
+        tracker.initialize(0)
+        val callback = TestCallback()
+
+        tracker.addCallback(callback, executor)
+
+        assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
+        assertThat(callback.calledOnUserChanged).isEqualTo(0)
+    }
+
+    @Test
+    fun testCallbackCalledOnUserChanged() {
+        tracker.initialize(0)
+        val callback = TestCallback()
+        tracker.addCallback(callback, executor)
+
+        val newID = 5
+
+        val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, newID)
+        tracker.onReceive(context, intent)
+
+        assertThat(callback.calledOnUserChanged).isEqualTo(1)
+        assertThat(callback.lastUser).isEqualTo(newID)
+        assertThat(callback.lastUserContext?.userId).isEqualTo(newID)
+        assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
+        assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(newID)
+    }
+
+    @Test
+    fun testCallbackCalledOnProfileChanged() {
+        tracker.initialize(0)
+        val callback = TestCallback()
+        tracker.addCallback(callback, executor)
+        val profileID = tracker.userId + 10
+
+        `when`(userManager.getProfiles(anyInt())).thenAnswer { invocation ->
+            val id = invocation.getArgument<Int>(0)
+            val info = UserInfo(id, "", UserInfo.FLAG_FULL)
+            val infoProfile = UserInfo(
+                    id + 10,
+                    "",
+                    "",
+                    UserInfo.FLAG_MANAGED_PROFILE,
+                    UserManager.USER_TYPE_PROFILE_MANAGED
+            )
+            infoProfile.profileGroupId = id
+            listOf(info, infoProfile)
+        }
+
+        val intent = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+
+        tracker.onReceive(context, intent)
+
+        assertThat(callback.calledOnUserChanged).isEqualTo(0)
+        assertThat(callback.calledOnProfilesChanged).isEqualTo(1)
+        assertThat(callback.lastUserProfiles.map { it.id }).containsExactly(0, profileID)
+    }
+
+    @Test
+    fun testCallbackRemoved() {
+        tracker.initialize(0)
+        val newID = 5
+        val profileID = newID + 10
+
+        val callback = TestCallback()
+        tracker.addCallback(callback, executor)
+        tracker.removeCallback(callback)
+
+        val intent = Intent(Intent.ACTION_USER_SWITCHED).putExtra(Intent.EXTRA_USER_HANDLE, 5)
+        tracker.onReceive(context, intent)
+
+        val intentProfiles = Intent(Intent.ACTION_MANAGED_PROFILE_AVAILABLE)
+                .putExtra(Intent.EXTRA_USER, UserHandle.of(profileID))
+
+        tracker.onReceive(context, intentProfiles)
+
+        assertThat(callback.calledOnUserChanged).isEqualTo(0)
+        assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
+    }
+
+    private class TestCallback : UserTracker.Callback {
+        var calledOnUserChanged = 0
+        var calledOnProfilesChanged = 0
+        var lastUser: Int? = null
+        var lastUserContext: Context? = null
+        var lastUserProfiles = emptyList<UserInfo>()
+
+        override fun onUserChanged(newUser: Int, userContext: Context) {
+            calledOnUserChanged++
+            lastUser = newUser
+            lastUserContext = userContext
+        }
+
+        override fun onProfilesChanged(profiles: List<UserInfo>) {
+            calledOnProfilesChanged++
+            lastUserProfiles = profiles
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 1259d28..d2bf483 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -19,7 +19,6 @@
 import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
@@ -219,7 +218,7 @@
                 mViewHierarchyManager.onDynamicPrivacyChanged();
             }
             return null;
-        }).when(mListContainer).setMaxDisplayedNotifications(anyInt());
+        }).when(mListContainer).onNotificationViewUpdateFinished();
 
         // WHEN we call updateNotificationViews()
         mViewHierarchyManager.updateNotificationViews();
@@ -247,7 +246,7 @@
                 mViewHierarchyManager.onDynamicPrivacyChanged();
             }
             return null;
-        }).when(mListContainer).setMaxDisplayedNotifications(anyInt());
+        }).when(mListContainer).onNotificationViewUpdateFinished();
 
         // WHEN we call updateNotificationViews() and drain the looper
         mViewHierarchyManager.updateNotificationViews();
@@ -262,7 +261,6 @@
     private class FakeListContainer implements NotificationListContainer {
         final LinearLayout mLayout = new LinearLayout(mContext);
         final List<View> mRows = Lists.newArrayList();
-        private boolean mMakeReentrantCallDuringSetMaxDisplayedNotifications;
 
         @Override
         public void setChildTransferInProgress(boolean childTransferInProgress) {}
@@ -322,9 +320,6 @@
 
         @Override
         public void setMaxDisplayedNotifications(int maxNotifications) {
-            if (mMakeReentrantCallDuringSetMaxDisplayedNotifications) {
-                mViewHierarchyManager.onDynamicPrivacyChanged();
-            }
         }
 
         @Override
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 d4718e7..fc0201a 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
@@ -346,7 +346,6 @@
         setSmartActions(mEntry.getKey(), null);
 
         mEntryManager.updateNotificationRanking(mRankingMap);
-        verify(mRow, never()).setEntry(eq(mEntry));
         assertNull(mEntry.getSmartActions());
     }
 
@@ -360,7 +359,6 @@
         setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
 
         mEntryManager.updateNotificationRanking(mRankingMap);
-        verify(mRow, never()).setEntry(eq(mEntry));
         assertEquals(1, mEntry.getSmartActions().size());
         assertEquals("action", mEntry.getSmartActions().get(0).title);
     }
@@ -375,7 +373,6 @@
         setSmartActions(mEntry.getKey(), new ArrayList<>(Arrays.asList(createAction())));
 
         mEntryManager.updateNotificationRanking(mRankingMap);
-        verify(mRow, never()).setEntry(eq(mEntry));
         assertEquals(1, mEntry.getSmartActions().size());
         assertEquals("action", mEntry.getSmartActions().get(0).title);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index c2c40ca..e1668ca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -69,7 +69,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.settings.CurrentUserContextTracker;
+import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.notification.AssistantFeedbackController;
@@ -128,7 +128,7 @@
     @Mock private ShortcutManager mShortcutManager;
     @Mock private ChannelEditorDialogController mChannelEditorDialogController;
     @Mock private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
-    @Mock private CurrentUserContextTracker mContextTracker;
+    @Mock private UserContextProvider mContextTracker;
     @Mock private BubbleController mBubbleController;
     @Mock(answer = Answers.RETURNS_SELF)
     private PriorityOnboardingDialogController.Builder mBuilder;
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 9a8678f0..df26c5b 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
@@ -406,12 +406,12 @@
 
         entry.setRow(row);
         mIconManager.createIcons(entry);
-        row.setEntry(entry);
 
         mBindPipelineEntryListener.onEntryInit(entry);
         mBindPipeline.manageRow(entry, row);
 
         row.initialize(
+                entry,
                 APP_NAME,
                 entry.getKey(),
                 mock(ExpansionLogger.class),
@@ -426,6 +426,7 @@
                 mStatusBarStateController,
                 mPeopleNotificationIdentifier,
                 mock(OnUserInteractionCallback.class));
+
         row.setAboveShelfChangedListener(aboveShelf -> { });
         mBindStage.getStageParams(entry).requireContentViews(extraInflationFlags);
         inflateAndWait(entry);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index bca7b31..62b741c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -18,7 +18,6 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
-import static junit.framework.Assert.assertNull;
 
 import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.any;
@@ -34,12 +33,10 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.metrics.LogMaker;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.view.View;
 
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.SmallTest;
@@ -52,6 +49,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationMediaManager;
@@ -80,12 +78,10 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.FooterView;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
-import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.KeyguardBypassEnabledProvider;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.util.leak.LeakDetector;
@@ -124,7 +120,6 @@
     @Mock private EmptyShadeView mEmptyShadeView;
     @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private RemoteInputController mRemoteInputController;
-    @Mock private NotificationIconAreaController mNotificationIconAreaController;
     @Mock private MetricsLogger mMetricsLogger;
     @Mock private NotificationRoundnessManager mNotificationRoundnessManager;
     @Mock private KeyguardBypassEnabledProvider mKeyguardBypassEnabledProvider;
@@ -132,6 +127,7 @@
     @Mock private NotificationSection mNotificationSection;
     @Mock private FeatureFlags mFeatureFlags;
     @Mock private SysuiStatusBarStateController mStatusBarStateController;
+    @Mock private NotificationSwipeHelper mNotificationSwipeHelper;
     private NotificationEntryManager mEntryManager;
     private int mOriginalInterruptionModelSetting;
     private UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
@@ -204,9 +200,6 @@
                 mNotificationRoundnessManager,
                 mock(DynamicPrivacyController.class),
                 mStatusBarStateController,
-                mHeadsUpManager,
-                new FalsingManagerFake(),
-                mock(NotificationGutsManager.class),
                 mNotificationSectionsManager,
                 mock(ForegroundServiceSectionController.class),
                 mock(ForegroundServiceDismissalFeatureController.class),
@@ -216,11 +209,11 @@
                 mock(NotifCollection.class),
                 mUiEventLoggerFake
         );
-        mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider);
+        mStackScrollerInternal.initView(getContext(), mKeyguardBypassEnabledProvider,
+                mNotificationSwipeHelper);
         mStackScroller = spy(mStackScrollerInternal);
         mStackScroller.setShelfController(notificationShelfController);
         mStackScroller.setStatusBar(mBar);
-        mStackScroller.setScrimController(mock(ScrimController.class));
         mStackScroller.setGroupManager(mGroupManager);
         mStackScroller.setEmptyShadeView(mEmptyShadeView);
 
@@ -420,31 +413,22 @@
     @Test
     @UiThreadTest
     public void testSetIsBeingDraggedResetsExposedMenu() {
-        NotificationSwipeHelper swipeActionHelper =
-                (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
-        swipeActionHelper.setExposedMenuView(new View(mContext));
         mStackScroller.setIsBeingDragged(true);
-        assertNull(swipeActionHelper.getExposedMenuView());
+        verify(mNotificationSwipeHelper).resetExposedMenuView(true, true);
     }
 
     @Test
     @UiThreadTest
     public void testPanelTrackingStartResetsExposedMenu() {
-        NotificationSwipeHelper swipeActionHelper =
-                (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
-        swipeActionHelper.setExposedMenuView(new View(mContext));
         mStackScroller.onPanelTrackingStarted();
-        assertNull(swipeActionHelper.getExposedMenuView());
+        verify(mNotificationSwipeHelper).resetExposedMenuView(true, true);
     }
 
     @Test
     @UiThreadTest
     public void testDarkModeResetsExposedMenu() {
-        NotificationSwipeHelper swipeActionHelper =
-                (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
-        swipeActionHelper.setExposedMenuView(new View(mContext));
         mStackScroller.setHideAmount(0.1f, 0.1f);
-        assertNull(swipeActionHelper.getExposedMenuView());
+        verify(mNotificationSwipeHelper).resetExposedMenuView(true, true);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
index 83d6ac9..f5d9fa0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.res.Resources;
 import android.metrics.LogMaker;
 import android.testing.AndroidTestingRunner;
 
@@ -36,6 +37,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.media.KeyguardMediaController;
+import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -47,6 +49,9 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.tuner.TunerService;
@@ -54,6 +59,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Answers;
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
@@ -94,6 +100,20 @@
     private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
     @Mock
     private MetricsLogger mMetricsLogger;
+    @Mock
+    private FalsingManager mFalsingManager;
+    @Mock
+    private NotificationSectionsManager mNotificationSectionsManager;
+    @Mock
+    private Resources mResources;
+    @Mock(answer = Answers.RETURNS_SELF)
+    private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
+    @Mock
+    private NotificationSwipeHelper mNotificationSwipeHelper;
+    @Mock
+    private StatusBar mStatusBar;
+    @Mock
+    private ScrimController mScrimController;
 
     private NotificationStackScrollLayoutController mController;
 
@@ -101,6 +121,8 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
 
+        when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
+
         mController = new NotificationStackScrollLayoutController(
                 true,
                 mNotificationGutsManager,
@@ -115,7 +137,13 @@
                 mZenModeController,
                 mColorExtractor,
                 mNotificationLockscreenUserManager,
-                mMetricsLogger
+                mMetricsLogger,
+                mFalsingManager,
+                mNotificationSectionsManager,
+                mResources,
+                mNotificationSwipeHelperBuilder,
+                mStatusBar,
+                mScrimController
         );
 
         when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(true);
@@ -233,7 +261,7 @@
                 ArgumentCaptor.forClass(OnMenuEventListener.class);
 
         mController.attach(mNotificationStackScrollLayout);
-        verify(mNotificationStackScrollLayout).setMenuEventListener(
+        verify(mNotificationSwipeHelperBuilder).setOnMenuEventListener(
                 onMenuEventListenerArgumentCaptor.capture());
 
         OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue();
@@ -255,7 +283,7 @@
                 ArgumentCaptor.forClass(OnMenuEventListener.class);
 
         mController.attach(mNotificationStackScrollLayout);
-        verify(mNotificationStackScrollLayout).setMenuEventListener(
+        verify(mNotificationSwipeHelperBuilder).setOnMenuEventListener(
                 onMenuEventListenerArgumentCaptor.capture());
 
         OnMenuEventListener onMenuEventListener = onMenuEventListenerArgumentCaptor.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 323d8bd..0faf5d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -35,6 +35,7 @@
 import android.testing.TestableLooper;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 
 import androidx.test.filters.SmallTest;
 
@@ -78,7 +79,8 @@
         mCallback = mock(NotificationSwipeHelper.NotificationCallback.class);
         mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class);
         mSwipeHelper = spy(new NotificationSwipeHelper(
-                SwipeHelper.X, mCallback, mContext, mListener, new FalsingManagerFake()));
+                mContext.getResources(), ViewConfiguration.get(mContext),
+                new FalsingManagerFake(), SwipeHelper.X, mCallback, mListener));
         mView = mock(View.class);
         mEvent = mock(MotionEvent.class);
         mMenuRow = mock(NotificationMenuRowPlugin.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index c9e9d94..453baa5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -52,7 +52,9 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.keyguard.KeyguardClockSwitch;
 import com.android.keyguard.KeyguardClockSwitchController;
+import com.android.keyguard.KeyguardStatusView;
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.dagger.KeyguardStatusViewComponent;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingManagerFake;
@@ -110,8 +112,6 @@
     @Mock
     private ViewGroup mBigClockContainer;
     @Mock
-    private ScrimController mScrimController;
-    @Mock
     private NotificationIconAreaController mNotificationAreaController;
     @Mock
     private HeadsUpManagerPhone mHeadsUpManager;
@@ -186,10 +186,16 @@
     @Mock
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     @Mock
+    private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
+    @Mock
+    private KeyguardStatusViewComponent mKeyguardStatusViewComponent;
+    @Mock
     private KeyguardClockSwitchController mKeyguardClockSwitchController;
     @Mock
     private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
 
+    private FlingAnimationUtils.Builder mFlingAnimationUtilsBuilder;
+
     private NotificationPanelViewController mNotificationPanelViewController;
     private View.AccessibilityDelegate mAccessibiltyDelegate;
 
@@ -217,6 +223,8 @@
         when(mKeyguardBottomArea.getRightView()).thenReturn(mock(KeyguardAffordanceView.class));
         when(mView.findViewById(R.id.big_clock_container)).thenReturn(mBigClockContainer);
         when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame);
+        when(mView.findViewById(R.id.keyguard_status_view))
+                .thenReturn(mock(KeyguardStatusView.class));
         FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder(
                 mDisplayMetrics);
 
@@ -238,7 +246,12 @@
                 mock(NotificationRoundnessManager.class),
                 mStatusBarStateController,
                 new FalsingManagerFake());
+        when(mKeyguardStatusViewComponentFactory.build(any()))
+                .thenReturn(mKeyguardStatusViewComponent);
+        when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController())
+                .thenReturn(mKeyguardClockSwitchController);
         mNotificationPanelViewController = new NotificationPanelViewController(mView,
+                mResources,
                 mInjectionInflationController,
                 coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
                 mFalsingManager, mShadeController,
@@ -250,14 +263,13 @@
                 flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
                 mConversationNotificationManager, mMediaHiearchyManager,
                 mBiometricUnlockController, mStatusBarKeyguardViewManager,
-                () -> mKeyguardClockSwitchController,
                 mNotificationStackScrollLayoutController,
-                mNotificationAreaController);
+                mNotificationAreaController,
+                mKeyguardStatusViewComponentFactory);
         mNotificationPanelViewController.initDependencies(
                 mStatusBar,
                 mGroupManager,
-                mNotificationShelfController,
-                mScrimController);
+                mNotificationShelfController);
         mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
         mNotificationPanelViewController.setBar(mPanelBar);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index c1d51f3..25af584 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -46,6 +46,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.InjectionInflationController;
@@ -85,6 +86,7 @@
     @Mock private NotificationShadeDepthController mNotificationShadeDepthController;
     @Mock private SuperStatusBarViewFactory mStatusBarViewFactory;
     @Mock private NotificationShadeWindowController mNotificationShadeWindowController;
+    @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
 
     @Before
     public void setUp() {
@@ -123,11 +125,11 @@
                 mNotificationShadeDepthController,
                 mView,
                 mNotificationPanelViewController,
-                mStatusBarViewFactory);
+                mStatusBarViewFactory,
+                mNotificationStackScrollLayoutController);
         mController.setupExpandedStatusBar();
         mController.setService(mStatusBar, mNotificationShadeWindowController);
         mController.setDragDownHelper(mDragDownHelper);
-
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index ccc3078..2f45113 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -43,6 +43,7 @@
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.keyguard.FaceAuthScreenBrightnessController;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import com.android.systemui.plugins.FalsingManager;
@@ -58,6 +59,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Optional;
+
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -89,6 +92,8 @@
     private View mNotificationContainer;
     @Mock
     private KeyguardBypassController mBypassController;
+    @Mock
+    private FaceAuthScreenBrightnessController mFaceAuthScreenBrightnessController;
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
 
     @Before
@@ -108,6 +113,7 @@
                 mock(DockManager.class),
                 mock(NotificationShadeWindowController.class),
                 mKeyguardStateController,
+                mFaceAuthScreenBrightnessController,
                 mock(NotificationMediaManager.class));
         mStatusBarKeyguardViewManager.registerStatusBar(mStatusBar, mContainer,
                 mNotificationPanelView, mBiometrucUnlockController, mDismissCallbackRegistry,
@@ -274,11 +280,12 @@
                 DockManager dockManager,
                 NotificationShadeWindowController notificationShadeWindowController,
                 KeyguardStateController keyguardStateController,
+                FaceAuthScreenBrightnessController faceAuthScreenBrightnessController,
                 NotificationMediaManager notificationMediaManager) {
             super(context, callback, lockPatternUtils, sysuiStatusBarStateController,
                     configurationController, keyguardUpdateMonitor, navigationModeController,
                     dockManager, notificationShadeWindowController, keyguardStateController,
-                    notificationMediaManager);
+                    Optional.of(faceAuthScreenBrightnessController), notificationMediaManager);
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 8462386..87aee3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -96,7 +96,7 @@
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.stackdivider.SplitScreen;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationListener;
@@ -238,7 +238,7 @@
     @Mock private StatusBarComponent.Builder mStatusBarComponentBuilder;
     @Mock private StatusBarComponent mStatusBarComponent;
     @Mock private PluginManager mPluginManager;
-    @Mock private Divider mDivider;
+    @Mock private SplitScreen mSplitScreen;
     @Mock private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
     @Mock private LightsOutNotifController mLightsOutNotifController;
     @Mock private ViewMediatorCallback mViewMediatorCallback;
@@ -397,7 +397,7 @@
                 Optional.of(mRecents),
                 mStatusBarComponentBuilderProvider,
                 mPluginManager,
-                Optional.of(mDivider),
+                Optional.of(mSplitScreen),
                 mLightsOutNotifController,
                 mStatusBarNotificationActivityStarterBuilder,
                 mShadeController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index fbbfa96..3dd36d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -41,6 +41,7 @@
 import com.android.settingslib.bluetooth.LocalBluetoothProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -58,6 +59,7 @@
     private CachedBluetoothDeviceManager mMockDeviceManager;
     private LocalBluetoothAdapter mMockAdapter;
     private TestableLooper mTestableLooper;
+    private DumpManager mMockDumpManager;
     private BluetoothControllerImpl mBluetoothControllerImpl;
 
     private List<CachedBluetoothDevice> mDevices;
@@ -75,8 +77,10 @@
         when(mMockBluetoothManager.getEventManager()).thenReturn(mock(BluetoothEventManager.class));
         when(mMockBluetoothManager.getProfileManager())
                 .thenReturn(mock(LocalBluetoothProfileManager.class));
+        mMockDumpManager = mock(DumpManager.class);
 
         mBluetoothControllerImpl = new BluetoothControllerImpl(mContext,
+                mMockDumpManager,
                 mTestableLooper.getLooper(),
                 mTestableLooper.getLooper(),
                 mMockBluetoothManager);
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
index 070626be..c70dfcc 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/EventDispatcher.java
@@ -256,6 +256,7 @@
                 return actionMasked;
         }
     }
+
     /**
      * Sends down events to the view hierarchy for all pointers which are not already being
      * delivered i.e. pointers that are not yet injected.
@@ -285,6 +286,74 @@
     }
 
     /**
+     * Sends down events to the view hierarchy for all pointers which are not already being
+     * delivered with original down location. i.e. pointers that are not yet injected.
+     *
+     * @param prototype The prototype from which to create the injected events.
+     * @param policyFlags The policy flags associated with the event.
+     */
+    void sendDownForAllNotInjectedPointersWithOriginalDown(MotionEvent prototype, int policyFlags) {
+        // Inject the injected pointers.
+        int pointerIdBits = 0;
+        final int pointerCount = prototype.getPointerCount();
+        final MotionEvent event = computeEventWithOriginalDown(prototype);
+        for (int i = 0; i < pointerCount; i++) {
+            final int pointerId = prototype.getPointerId(i);
+            // Do not send event for already delivered pointers.
+            if (!mState.isInjectedPointerDown(pointerId)) {
+                pointerIdBits |= (1 << pointerId);
+                final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i);
+                sendMotionEvent(
+                        event,
+                        action,
+                        mState.getLastReceivedEvent(),
+                        pointerIdBits,
+                        policyFlags);
+            }
+        }
+    }
+
+    private MotionEvent computeEventWithOriginalDown(MotionEvent prototype) {
+        final int pointerCount = prototype.getPointerCount();
+        if (pointerCount != mState.getReceivedPointerTracker().getReceivedPointerDownCount()) {
+            Slog.w(LOG_TAG, "The pointer count doesn't match the received count.");
+            return MotionEvent.obtain(prototype);
+        }
+        MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[pointerCount];
+        MotionEvent.PointerProperties[] properties =
+                new MotionEvent.PointerProperties[pointerCount];
+        for (int i = 0; i < pointerCount; ++i) {
+            final int pointerId = prototype.getPointerId(i);
+            final float x = mState.getReceivedPointerTracker().getReceivedPointerDownX(pointerId);
+            final float y = mState.getReceivedPointerTracker().getReceivedPointerDownY(pointerId);
+            coords[i] = new MotionEvent.PointerCoords();
+            coords[i].x = x;
+            coords[i].y = y;
+            properties[i] = new MotionEvent.PointerProperties();
+            properties[i].id = pointerId;
+            properties[i].toolType = MotionEvent.TOOL_TYPE_FINGER;
+        }
+        MotionEvent event =
+                MotionEvent.obtain(
+                        prototype.getDownTime(),
+                        prototype.getEventTime(),
+                        prototype.getAction(),
+                        pointerCount,
+                        properties,
+                        coords,
+                        prototype.getMetaState(),
+                        prototype.getButtonState(),
+                        prototype.getXPrecision(),
+                        prototype.getYPrecision(),
+                        prototype.getDeviceId(),
+                        prototype.getEdgeFlags(),
+                        prototype.getSource(),
+                        prototype.getFlags());
+        return event;
+    }
+
+    /**
+     *
      * Sends up events to the view hierarchy for all pointers which are already being delivered i.e.
      * pointers that are injected.
      *
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
index 07e8211..5b46cb4 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
@@ -294,7 +294,7 @@
                                 + Float.toString(mGestureDetectionThresholdPixels));
             }
             if (getState() == STATE_CLEAR) {
-                if (moveDelta < mTouchSlop) {
+                if (moveDelta < (mTargetFingerCount * mTouchSlop)) {
                     // This still counts as a touch not a swipe.
                     continue;
                 } else if (mStrokeBuffers[pointerIndex].size() == 0) {
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index b587dd3..8305be3 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -594,15 +594,23 @@
                     if (pointerIndex < 0) {
                         return;
                     }
-                    final float deltaX =
-                            mReceivedPointerTracker.getReceivedPointerDownX(pointerId)
-                                    - rawEvent.getX(pointerIndex);
-                    final float deltaY =
-                            mReceivedPointerTracker.getReceivedPointerDownY(pointerId)
-                                    - rawEvent.getY(pointerIndex);
-                    final double moveDelta = Math.hypot(deltaX, deltaY);
-                    if (moveDelta < mTouchSlop) {
-                        return;
+                    // Require both fingers to have moved a certain amount before starting a drag.
+                    for (int index = 0; index < event.getPointerCount(); ++index) {
+                        int id = event.getPointerId(index);
+                        if (!mReceivedPointerTracker.isReceivedPointerDown(id)) {
+                            // Something is wrong with the event stream.
+                            Slog.e(LOG_TAG, "Invalid pointer id: " + id);
+                        }
+                        final float deltaX =
+                                mReceivedPointerTracker.getReceivedPointerDownX(id)
+                                        - rawEvent.getX(index);
+                        final float deltaY =
+                                mReceivedPointerTracker.getReceivedPointerDownY(id)
+                                        - rawEvent.getY(index);
+                        final double moveDelta = Math.hypot(deltaX, deltaY);
+                        if (moveDelta < (2 * mTouchSlop)) {
+                            return;
+                        }
                     }
                 }
                 // More than one pointer so the user is not touch exploring
@@ -612,12 +620,20 @@
                 if (isDraggingGesture(event)) {
                     // Two pointers moving in the same direction within
                     // a given distance perform a drag.
-                    mState.startDragging();
                     computeDraggingPointerIdIfNeeded(event);
                     pointerIdBits = 1 << mDraggingPointerId;
                     event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags());
-                    mDispatcher.sendMotionEvent(
-                            event, ACTION_DOWN, rawEvent, pointerIdBits, policyFlags);
+                    MotionEvent downEvent = computeDownEventForDrag(event);
+                    if (downEvent != null) {
+                        mDispatcher.sendMotionEvent(
+                                downEvent, ACTION_DOWN, rawEvent, pointerIdBits, policyFlags);
+                        mDispatcher.sendMotionEvent(
+                                event, ACTION_MOVE, rawEvent, pointerIdBits, policyFlags);
+                    } else {
+                        mDispatcher.sendMotionEvent(
+                                event, ACTION_DOWN, rawEvent, pointerIdBits, policyFlags);
+                    }
+                    mState.startDragging();
                 } else {
                     // Two pointers moving arbitrary are delegated to the view hierarchy.
                     mState.startDelegating();
@@ -628,23 +644,20 @@
                 if (mGestureDetector.isMultiFingerGesturesEnabled()) {
                     if (mGestureDetector.isTwoFingerPassthroughEnabled()) {
                         if (event.getPointerCount() == 3) {
-                            boolean isOnBottomEdge = true;
                             // If three fingers went down on the bottom edge of the screen, delegate
                             // immediately.
-                            final long screenHeight =
-                                    mContext.getResources().getDisplayMetrics().heightPixels;
-                            for (int i = 0; i < TouchState.MAX_POINTER_COUNT; ++i) {
-                                if (mReceivedPointerTracker.getReceivedPointerDownY(i)
-                                        < (screenHeight - mEdgeSwipeHeightPixels)) {
-                                    isOnBottomEdge = false;
-                                }
-                            }
-                            if (isOnBottomEdge) {
+                            if (allPointersDownOnBottomEdge(event)) {
                                 if (DEBUG) {
                                     Slog.d(LOG_TAG, "Three-finger edge swipe detected.");
                                 }
                                 mState.startDelegating();
-                                mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags);
+                                if (mState.isTouchExploring()) {
+                                    mDispatcher.sendDownForAllNotInjectedPointers(event,
+                                            policyFlags);
+                                } else {
+                                    mDispatcher.sendDownForAllNotInjectedPointersWithOriginalDown(
+                                            event, policyFlags);
+                                }
                             }
                         }
                     }
@@ -1004,6 +1017,65 @@
         return distance;
     }
 
+    /**
+     * Creates a down event using the down coordinates of the dragging pointer and other information
+     * from the supplied event. The supplied event's down time is adjusted to reflect the time when
+     * the dragging pointer initially went down.
+     */
+    private MotionEvent computeDownEventForDrag(MotionEvent event) {
+        // Creating a down event only  makes sense if we haven't started touch exploring yet.
+        if (mState.isTouchExploring()
+                || mDraggingPointerId == INVALID_POINTER_ID
+                || event == null) {
+            return null;
+        }
+        final float x = mReceivedPointerTracker.getReceivedPointerDownX(mDraggingPointerId);
+        final float y = mReceivedPointerTracker.getReceivedPointerDownY(mDraggingPointerId);
+        final long time = mReceivedPointerTracker.getReceivedPointerDownTime(mDraggingPointerId);
+        MotionEvent.PointerCoords[] coords = new MotionEvent.PointerCoords[1];
+        coords[0] = new MotionEvent.PointerCoords();
+        coords[0].x = x;
+        coords[0].y = y;
+        MotionEvent.PointerProperties[] properties = new MotionEvent.PointerProperties[1];
+        properties[0] = new MotionEvent.PointerProperties();
+        properties[0].id = mDraggingPointerId;
+        properties[0].toolType = MotionEvent.TOOL_TYPE_FINGER;
+        MotionEvent downEvent =
+                MotionEvent.obtain(
+                        time,
+                        time,
+                        ACTION_DOWN,
+                        1,
+                        properties,
+                        coords,
+                        event.getMetaState(),
+                        event.getButtonState(),
+                        event.getXPrecision(),
+                        event.getYPrecision(),
+                        event.getDeviceId(),
+                        event.getEdgeFlags(),
+                        event.getSource(),
+                        event.getFlags());
+        event.setDownTime(time);
+        return downEvent;
+    }
+
+    private boolean allPointersDownOnBottomEdge(MotionEvent event) {
+        final long screenHeight =
+                mContext.getResources().getDisplayMetrics().heightPixels;
+        for (int i = 0; i < event.getPointerCount(); ++i) {
+            final int pointerId = event.getPointerId(i);
+            final float pointerDownY = mReceivedPointerTracker.getReceivedPointerDownY(pointerId);
+            if (pointerDownY < (screenHeight - mEdgeSwipeHeightPixels)) {
+                if (DEBUG) {
+                    Slog.d(LOG_TAG, "The pointer is not on the bottom edge" + pointerDownY);
+                }
+                return false;
+            }
+        }
+        return true;
+    }
+
     public TouchState getState() {
         return mState;
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 3ee5b28..7d6067c 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -176,7 +176,7 @@
             Slog.i(LOG_TAG, "onDestroy(); delayed = "
                     + mDetectingState.toString());
         }
-        mWindowMagnificationMgr.disableWindowMagnifier(mDisplayId, true);
+        mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, true);
         resetToDetectState();
     }
 
@@ -211,14 +211,14 @@
         final float scale = MathUtils.constrain(
                 mWindowMagnificationMgr.getPersistedScale(),
                 MIN_SCALE, MAX_SCALE);
-        mWindowMagnificationMgr.enableWindowMagnifier(mDisplayId, scale, centerX, centerY);
+        mWindowMagnificationMgr.enableWindowMagnification(mDisplayId, scale, centerX, centerY);
     }
 
     private void disableWindowMagnifier() {
         if (DEBUG_ALL) {
             Slog.i(LOG_TAG, "disableWindowMagnifier()");
         }
-        mWindowMagnificationMgr.disableWindowMagnifier(mDisplayId, false);
+        mWindowMagnificationMgr.disableWindowMagnification(mDisplayId, false);
     }
 
     private void toggleMagnification(float centerX, float centerY) {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index ed2b26f..ecbece6 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -73,7 +73,7 @@
         public void onReceive(Context context, Intent intent) {
             final int displayId = context.getDisplayId();
             removeMagnificationButton(displayId);
-            disableWindowMagnification(displayId);
+            disableWindowMagnification(displayId, false);
         }
     };
 
@@ -136,10 +136,10 @@
     /**
      * Requests {@link IWindowMagnificationConnection} through
      * {@link StatusBarManagerInternal#requestWindowMagnificationConnection(boolean)} and
-     * destroys all window magnifiers if necessary.
+     * destroys all window magnifications if necessary.
      *
      * @param connect {@code true} if needs connection, otherwise set the connection to null and
-     *                            destroy all window magnifiers.
+     *                            destroy all window magnifications.
      * @return {@code true} if {@link IWindowMagnificationConnection} state is going to change.
      */
     public boolean requestConnection(boolean connect) {
@@ -171,7 +171,7 @@
     private void disableAllWindowMagnifiers() {
         for (int i = 0; i < mWindowMagnifiers.size(); i++) {
             final WindowMagnifier magnifier = mWindowMagnifiers.valueAt(i);
-            magnifier.disable();
+            magnifier.disableWindowMagnificationInternal();
         }
         mWindowMagnifiers.clear();
     }
@@ -187,12 +187,12 @@
 
     @Override
     public boolean processScroll(int displayId, float distanceX, float distanceY) {
-        moveWindowMagnifier(displayId, -distanceX, -distanceY);
+        moveWindowMagnification(displayId, -distanceX, -distanceY);
         return /* event consumed: */ true;
     }
 
     /**
-     * Scales the magnified region on the specified display if the window magnifier is enabled.
+     * Scales the magnified region on the specified display if the window magnifier is initiated.
      *
      * @param displayId The logical display id.
      * @param scale The target scale, must be >= 1
@@ -209,7 +209,7 @@
     }
 
     /**
-     * Enables the window magnifier with specified center and scale on the specified display.
+     * Enables window magnification with specified center and scale on the specified display.
      *  @param displayId The logical display id.
      * @param scale The target scale, must be >= 1.
      * @param centerX The screen-relative X coordinate around which to center,
@@ -217,29 +217,29 @@
      * @param centerY The screen-relative Y coordinate around which to center,
      *                or {@link Float#NaN} to leave unchanged.
      */
-    void enableWindowMagnifier(int displayId, float scale, float centerX, float centerY) {
+    void enableWindowMagnification(int displayId, float scale, float centerX, float centerY) {
         synchronized (mLock) {
             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
             if (magnifier == null) {
                 magnifier = createWindowMagnifier(displayId);
             }
-            magnifier.enable(scale, centerX, centerY);
+            magnifier.enableWindowMagnificationInternal(scale, centerX, centerY);
         }
     }
 
     /**
-     * Disables the window magnifier on the specified display.
+     * Disables window magnification on the specified display.
      *
      * @param displayId The logical display id.
      * @param clear {@true} Clears the state of the window magnifier
      */
-    void disableWindowMagnifier(int displayId, boolean clear) {
+    void disableWindowMagnification(int displayId, boolean clear) {
         synchronized (mLock) {
             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
             if (magnifier == null) {
                 return;
             }
-            magnifier.disable();
+            magnifier.disableWindowMagnificationInternal();
             if (clear) {
                 mWindowMagnifiers.delete(displayId);
             }
@@ -264,10 +264,10 @@
     }
 
     /**
-     * Indicates whether this window magnifier is enabled on specified display.
+     * Indicates whether window magnification is enabled on specified display.
      *
      * @param displayId The logical display id.
-     * @return {@code true} if the window magnifier is enabled.
+     * @return {@code true} if the window magnification is enabled.
      */
     boolean isWindowMagnifierEnabled(int displayId) {
         synchronized (mLock) {
@@ -323,7 +323,7 @@
     }
 
     /**
-     * Moves the window magnifier with specified offset.
+     * Moves window magnification on the specified display with the specified offset.
      *
      * @param displayId The logical display id.
      * @param offsetX the amount in pixels to offset the region in the X direction, in current
@@ -331,7 +331,7 @@
      * @param offsetY the amount in pixels to offset the region in the Y direction, in current
      *                screen pixels.
      */
-    void moveWindowMagnifier(int displayId, float offsetX, float offsetY) {
+    void moveWindowMagnification(int displayId, float offsetX, float offsetY) {
         synchronized (mLock) {
             WindowMagnifier magnifier = mWindowMagnifiers.get(displayId);
             if (magnifier == null) {
@@ -425,7 +425,8 @@
     }
 
     /**
-     * A class to  manipulate the window magnifier and contains the relevant information.
+     * A class manipulates window magnification per display and contains the magnification
+     * information.
      */
     private static class WindowMagnifier {
 
@@ -434,7 +435,7 @@
         private boolean mEnabled;
 
         private final WindowMagnificationManager mWindowMagnificationManager;
-        //Records the bounds of window magnifier.
+        //Records the bounds of window magnification.
         private final Rect mBounds = new Rect();
         //The magnified bounds on the screen.
         private final Rect mSourceBounds = new Rect();
@@ -444,12 +445,12 @@
         }
 
         @GuardedBy("mLock")
-        void enable(float scale, float centerX, float centerY) {
+        void enableWindowMagnificationInternal(float scale, float centerX, float centerY) {
             if (mEnabled) {
                 return;
             }
             final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
-            if (mWindowMagnificationManager.enableWindowMagnification(mDisplayId, normScale,
+            if (mWindowMagnificationManager.enableWindowMagnificationInternal(mDisplayId, normScale,
                     centerX, centerY)) {
                 mScale = normScale;
                 mEnabled = true;
@@ -457,8 +458,9 @@
         }
 
         @GuardedBy("mLock")
-        void disable() {
-            if (mEnabled && mWindowMagnificationManager.disableWindowMagnification(mDisplayId)) {
+        void disableWindowMagnificationInternal() {
+            if (mEnabled && mWindowMagnificationManager.disableWindowMagnificationInternal(
+                    mDisplayId)) {
                 mEnabled = false;
             }
         }
@@ -519,7 +521,7 @@
         }
     }
 
-    private boolean enableWindowMagnification(int displayId, float scale, float centerX,
+    private boolean enableWindowMagnificationInternal(int displayId, float scale, float centerX,
             float centerY) {
         return mConnectionWrapper != null && mConnectionWrapper.enableWindowMagnification(
                 displayId, scale, centerX, centerY);
@@ -529,7 +531,7 @@
         return mConnectionWrapper != null && mConnectionWrapper.setScale(displayId, scale);
     }
 
-    private boolean disableWindowMagnification(int displayId) {
+    private boolean disableWindowMagnificationInternal(int displayId) {
         return mConnectionWrapper != null && mConnectionWrapper.disableWindowMagnification(
                 displayId);
     }
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
index 1c4db12..59ba82e 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
@@ -34,6 +34,7 @@
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.os.Binder;
+import android.os.IBinder;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.util.Slog;
@@ -108,9 +109,9 @@
 
         @Override
         public void createPredictionSession(@NonNull AppPredictionContext context,
-                @NonNull AppPredictionSessionId sessionId) {
-            runForUserLocked("createPredictionSession", sessionId,
-                    (service) -> service.onCreatePredictionSessionLocked(context, sessionId));
+                @NonNull AppPredictionSessionId sessionId, @NonNull IBinder token) {
+            runForUserLocked("createPredictionSession", sessionId, (service) ->
+                    service.onCreatePredictionSessionLocked(context, sessionId, token));
         }
 
         @Override
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 7ee607c..735f420 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -30,6 +30,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ServiceInfo;
+import android.os.IBinder;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.provider.DeviceConfig;
@@ -44,8 +45,6 @@
 import com.android.server.infra.AbstractPerUserSystemService;
 import com.android.server.people.PeopleServiceInternal;
 
-import java.util.function.Consumer;
-
 /**
  * Per-user instance of {@link AppPredictionManagerService}.
  */
@@ -112,17 +111,24 @@
      */
     @GuardedBy("mLock")
     public void onCreatePredictionSessionLocked(@NonNull AppPredictionContext context,
-            @NonNull AppPredictionSessionId sessionId) {
-        if (!mSessionInfos.containsKey(sessionId)) {
-            mSessionInfos.put(sessionId, new AppPredictionSessionInfo(sessionId, context,
-                    DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
-                            PREDICT_USING_PEOPLE_SERVICE_PREFIX + context.getUiSurface(), false),
-                    this::removeAppPredictionSessionInfo));
-        }
-        final boolean serviceExists = resolveService(sessionId, s ->
-                s.onCreatePredictionSession(context, sessionId), true);
-        if (!serviceExists) {
-            mSessionInfos.remove(sessionId);
+            @NonNull AppPredictionSessionId sessionId, @NonNull IBinder token) {
+        final boolean usesPeopleService = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
+                PREDICT_USING_PEOPLE_SERVICE_PREFIX + context.getUiSurface(), false);
+        final boolean serviceExists = resolveService(sessionId, false,
+                usesPeopleService, s -> s.onCreatePredictionSession(context, sessionId));
+        if (serviceExists && !mSessionInfos.containsKey(sessionId)) {
+            final AppPredictionSessionInfo sessionInfo = new AppPredictionSessionInfo(
+                    sessionId, context, usesPeopleService, token, () -> {
+                synchronized (mLock) {
+                    onDestroyPredictionSessionLocked(sessionId);
+                }
+            });
+            if (sessionInfo.linkToDeath()) {
+                mSessionInfos.put(sessionId, sessionInfo);
+            } else {
+                // destroy the session if calling process is already dead
+                onDestroyPredictionSessionLocked(sessionId);
+            }
         }
     }
 
@@ -132,7 +138,10 @@
     @GuardedBy("mLock")
     public void notifyAppTargetEventLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull AppTargetEvent event) {
-        resolveService(sessionId, s -> s.notifyAppTargetEvent(sessionId, event), false);
+        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (sessionInfo == null) return;
+        resolveService(sessionId, false, sessionInfo.mUsesPeopleService,
+                s -> s.notifyAppTargetEvent(sessionId, event));
     }
 
     /**
@@ -141,8 +150,10 @@
     @GuardedBy("mLock")
     public void notifyLaunchLocationShownLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
-        resolveService(sessionId, s ->
-                s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds), false);
+        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (sessionInfo == null) return;
+        resolveService(sessionId, false, sessionInfo.mUsesPeopleService,
+                s -> s.notifyLaunchLocationShown(sessionId, launchLocation, targetIds));
     }
 
     /**
@@ -151,7 +162,10 @@
     @GuardedBy("mLock")
     public void sortAppTargetsLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull ParceledListSlice targets, @NonNull IPredictionCallback callback) {
-        resolveService(sessionId, s -> s.sortAppTargets(sessionId, targets, callback), true);
+        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (sessionInfo == null) return;
+        resolveService(sessionId, true, sessionInfo.mUsesPeopleService,
+                s -> s.sortAppTargets(sessionId, targets, callback));
     }
 
     /**
@@ -160,10 +174,12 @@
     @GuardedBy("mLock")
     public void registerPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull IPredictionCallback callback) {
-        final boolean serviceExists = resolveService(sessionId, s ->
-                s.registerPredictionUpdates(sessionId, callback), false);
         final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
-        if (serviceExists && sessionInfo != null) {
+        if (sessionInfo == null) return;
+        final boolean serviceExists = resolveService(sessionId, false,
+                sessionInfo.mUsesPeopleService,
+                s -> s.registerPredictionUpdates(sessionId, callback));
+        if (serviceExists) {
             sessionInfo.addCallbackLocked(callback);
         }
     }
@@ -174,10 +190,12 @@
     @GuardedBy("mLock")
     public void unregisterPredictionUpdatesLocked(@NonNull AppPredictionSessionId sessionId,
             @NonNull IPredictionCallback callback) {
-        final boolean serviceExists = resolveService(sessionId, s ->
-                s.unregisterPredictionUpdates(sessionId, callback), false);
         final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
-        if (serviceExists && sessionInfo != null) {
+        if (sessionInfo == null) return;
+        final boolean serviceExists = resolveService(sessionId, false,
+                sessionInfo.mUsesPeopleService,
+                s -> s.unregisterPredictionUpdates(sessionId, callback));
+        if (serviceExists) {
             sessionInfo.removeCallbackLocked(callback);
         }
     }
@@ -187,7 +205,10 @@
      */
     @GuardedBy("mLock")
     public void requestPredictionUpdateLocked(@NonNull AppPredictionSessionId sessionId) {
-        resolveService(sessionId, s -> s.requestPredictionUpdate(sessionId), true);
+        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
+        if (sessionInfo == null) return;
+        resolveService(sessionId, true, sessionInfo.mUsesPeopleService,
+                s -> s.requestPredictionUpdate(sessionId));
     }
 
     /**
@@ -195,12 +216,14 @@
      */
     @GuardedBy("mLock")
     public void onDestroyPredictionSessionLocked(@NonNull AppPredictionSessionId sessionId) {
-        final boolean serviceExists = resolveService(sessionId, s ->
-                s.onDestroyPredictionSession(sessionId), false);
-        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
-        if (serviceExists && sessionInfo != null) {
-            sessionInfo.destroy();
+        if (isDebug()) {
+            Slog.d(TAG, "onDestroyPredictionSessionLocked(): sessionId=" + sessionId);
         }
+        final AppPredictionSessionInfo sessionInfo = mSessionInfos.remove(sessionId);
+        if (sessionInfo == null) return;
+        resolveService(sessionId, false, sessionInfo.mUsesPeopleService,
+                s -> s.onDestroyPredictionSession(sessionId));
+        sessionInfo.destroy();
     }
 
     @Override
@@ -291,27 +314,18 @@
         }
 
         for (AppPredictionSessionInfo sessionInfo : mSessionInfos.values()) {
-            sessionInfo.resurrectSessionLocked(this);
-        }
-    }
-
-    private void removeAppPredictionSessionInfo(AppPredictionSessionId sessionId) {
-        if (isDebug()) {
-            Slog.d(TAG, "removeAppPredictionSessionInfo(): sessionId=" + sessionId);
-        }
-        synchronized (mLock) {
-            mSessionInfos.remove(sessionId);
+            sessionInfo.resurrectSessionLocked(this, sessionInfo.mToken);
         }
     }
 
     @GuardedBy("mLock")
     @Nullable
-    protected boolean resolveService(@NonNull final AppPredictionSessionId sessionId,
-            @NonNull final AbstractRemoteService.AsyncRequest<IPredictionService> cb,
-            boolean sendImmediately) {
-        final AppPredictionSessionInfo sessionInfo = mSessionInfos.get(sessionId);
-        if (sessionInfo == null) return false;
-        if (sessionInfo.mUsesPeopleService) {
+    protected boolean resolveService(
+            @NonNull final AppPredictionSessionId sessionId,
+            boolean sendImmediately,
+            boolean usesPeopleService,
+            @NonNull final AbstractRemoteService.AsyncRequest<IPredictionService> cb) {
+        if (usesPeopleService) {
             final IPredictionService service =
                     LocalServices.getService(PeopleServiceInternal.class);
             if (service != null) {
@@ -368,7 +382,9 @@
         private final AppPredictionContext mPredictionContext;
         private final boolean mUsesPeopleService;
         @NonNull
-        private final Consumer<AppPredictionSessionId> mRemoveSessionInfoAction;
+        final IBinder mToken;
+        @NonNull
+        final IBinder.DeathRecipient mDeathRecipient;
 
         private final RemoteCallbackList<IPredictionCallback> mCallbacks =
                 new RemoteCallbackList<IPredictionCallback>() {
@@ -388,14 +404,16 @@
                 @NonNull final AppPredictionSessionId id,
                 @NonNull final AppPredictionContext predictionContext,
                 final boolean usesPeopleService,
-                @NonNull final Consumer<AppPredictionSessionId> removeSessionInfoAction) {
+                @NonNull final IBinder token,
+                @NonNull final IBinder.DeathRecipient deathRecipient) {
             if (DEBUG) {
                 Slog.d(TAG, "Creating AppPredictionSessionInfo for session Id=" + id);
             }
             mSessionId = id;
             mPredictionContext = predictionContext;
             mUsesPeopleService = usesPeopleService;
-            mRemoveSessionInfoAction = removeSessionInfoAction;
+            mToken = token;
+            mDeathRecipient = deathRecipient;
         }
 
         void addCallbackLocked(IPredictionCallback callback) {
@@ -414,23 +432,38 @@
             mCallbacks.unregister(callback);
         }
 
+        boolean linkToDeath() {
+            try {
+                mToken.linkToDeath(mDeathRecipient, 0);
+            } catch (RemoteException e) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Caller is dead before session can be started, sessionId: "
+                            + mSessionId);
+                }
+                return false;
+            }
+            return true;
+        }
+
         void destroy() {
             if (DEBUG) {
                 Slog.d(TAG, "Removing all callbacks for session Id=" + mSessionId
                         + " and " + mCallbacks.getRegisteredCallbackCount() + " callbacks.");
             }
+            if (mToken != null) {
+                mToken.unlinkToDeath(mDeathRecipient, 0);
+            }
             mCallbacks.kill();
-            mRemoveSessionInfoAction.accept(mSessionId);
         }
 
-        void resurrectSessionLocked(AppPredictionPerUserService service) {
+        void resurrectSessionLocked(AppPredictionPerUserService service, IBinder token) {
             int callbackCount = mCallbacks.getRegisteredCallbackCount();
             if (DEBUG) {
                 Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked()
                         + ") for session Id=" + mSessionId + " and "
                         + callbackCount + " callbacks.");
             }
-            service.onCreatePredictionSessionLocked(mPredictionContext, mSessionId);
+            service.onCreatePredictionSessionLocked(mPredictionContext, mSessionId, token);
             mCallbacks.broadcast(
                     callback -> service.registerPredictionUpdatesLocked(mSessionId, callback));
         }
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index ad1986a6..cf9324c 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -302,24 +302,11 @@
             String packageName, int userId);
 
     /**
-     * Do a straight uid lookup for the given package/application in the given user. This enforces
-     * app visibility rules and permissions. Call {@link #getPackageUidInternal} for the internal
-     * implementation.
-     * @deprecated Use {@link PackageManager#getPackageUid(String, int)}
-     * @return The app's uid, or < 0 if the package was not found in that user
-     */
-    @Deprecated
-    public abstract int getPackageUid(String packageName,
-            @PackageInfoFlags int flags, int userId);
-
-    /**
      * Do a straight uid lookup for the given package/application in the given user.
      * @see PackageManager#getPackageUidAsUser(String, int, int)
      * @return The app's uid, or < 0 if the package was not found in that user
-     * TODO(b/148235092): rename this to getPackageUid
      */
-    public abstract int getPackageUidInternal(String packageName,
-            @PackageInfoFlags int flags, int userId);
+    public abstract int getPackageUid(String packageName, @PackageInfoFlags int flags, int userId);
 
     /**
      * Retrieve all of the information we know about a particular package/application.
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index b72985c..27c5d4a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -2160,7 +2160,7 @@
             Slog.i(TAG, "Remounting storage for pid: " + pid);
             final String[] sharedPackages =
                     mPmInternal.getSharedUserPackagesForPackage(packageName, userId);
-            final int uid = mPmInternal.getPackageUidInternal(packageName, 0, userId);
+            final int uid = mPmInternal.getPackageUid(packageName, 0 /* flags */, userId);
             final String[] packages =
                     sharedPackages.length != 0 ? sharedPackages : new String[]{packageName};
             try {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 19871f9..eb8308b 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2106,20 +2106,20 @@
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
                 mOutgoingCallEmergencyNumber[phoneId] = emergencyNumber;
-                for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL)
-                                    && idMatch(r.subId, subId, phoneId)) {
-                        try {
-                            r.callback.onOutgoingEmergencyCall(emergencyNumber);
-                        } catch (RemoteException ex) {
-                            mRemoveList.add(r.binder);
-                        }
+            }
+            for (Record r : mRecords) {
+                // Send to all listeners regardless of subscription
+                if (r.matchPhoneStateListenerEvent(
+                        PhoneStateListener.LISTEN_OUTGOING_EMERGENCY_CALL)) {
+                    try {
+                        r.callback.onOutgoingEmergencyCall(emergencyNumber, subId);
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
                     }
                 }
             }
-            handleRemoveListLocked();
         }
+        handleRemoveListLocked();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 96d973e..687af10 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -85,6 +85,7 @@
 import java.util.Date;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 
 public class VibratorService extends IVibratorService.Stub
         implements InputManager.InputDeviceListener {
@@ -114,6 +115,9 @@
     private static final VibrationAttributes DEFAULT_ATTRIBUTES =
             new VibrationAttributes.Builder().build();
 
+    // Used to generate globally unique vibration ids.
+    private final AtomicInteger mNextVibrationId = new AtomicInteger(1); // 0 = no callback
+
     // A mapping from the intensity adjustment to the scaling to apply, where the intensity
     // adjustment is defined as the delta between the default intensity level and the user selected
     // intensity level. It's important that we apply the scaling on the delta between the two so
@@ -171,34 +175,34 @@
     private int mRingIntensity;
     private SparseArray<Vibration> mAlwaysOnEffects = new SparseArray<>();
 
-    static native long vibratorInit();
+    static native long vibratorInit(OnCompleteListener listener);
 
     static native long vibratorGetFinalizer();
 
-    static native boolean vibratorExists(long controllerPtr);
+    static native boolean vibratorExists(long nativeServicePtr);
 
-    static native void vibratorOn(long controllerPtr, long milliseconds, Vibration vibration);
+    static native void vibratorOn(long nativeServicePtr, long milliseconds, long vibrationId);
 
-    static native void vibratorOff(long controllerPtr);
+    static native void vibratorOff(long nativeServicePtr);
 
-    static native void vibratorSetAmplitude(long controllerPtr, int amplitude);
+    static native void vibratorSetAmplitude(long nativeServicePtr, int amplitude);
 
-    static native int[] vibratorGetSupportedEffects(long controllerPtr);
+    static native int[] vibratorGetSupportedEffects(long nativeServicePtr);
 
-    static native int[] vibratorGetSupportedPrimitives(long controllerPtr);
+    static native int[] vibratorGetSupportedPrimitives(long nativeServicePtr);
 
     static native long vibratorPerformEffect(
-            long controllerPtr, long effect, long strength, Vibration vibration);
+            long nativeServicePtr, long effect, long strength, long vibrationId);
 
-    static native void vibratorPerformComposedEffect(long controllerPtr,
-            VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration);
+    static native void vibratorPerformComposedEffect(long nativeServicePtr,
+            VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId);
 
-    static native void vibratorSetExternalControl(long controllerPtr, boolean enabled);
+    static native void vibratorSetExternalControl(long nativeServicePtr, boolean enabled);
 
-    static native long vibratorGetCapabilities(long controllerPtr);
-    static native void vibratorAlwaysOnEnable(long controllerPtr, long id, long effect,
+    static native long vibratorGetCapabilities(long nativeServicePtr);
+    static native void vibratorAlwaysOnEnable(long nativeServicePtr, long id, long effect,
             long strength);
-    static native void vibratorAlwaysOnDisable(long controllerPtr, long id);
+    static native void vibratorAlwaysOnDisable(long nativeServicePtr, long id);
 
     private final IUidObserver mUidObserver = new IUidObserver.Stub() {
         @Override public void onUidStateChanged(int uid, int procState, long procStateSeq,
@@ -220,12 +224,19 @@
         }
     };
 
+    /** Listener for vibration completion callbacks from native. */
+    public interface OnCompleteListener {
+
+        /** Callback triggered when vibration is complete, identified by {@link Vibration#id}. */
+        void onComplete(long vibrationId);
+    }
+
     /**
      * Holder for a vibration to be played. This class can be shared with native methods for
      * hardware callback support.
      */
-    @VisibleForTesting
-    public final class Vibration implements IBinder.DeathRecipient {
+    private final class Vibration implements IBinder.DeathRecipient {
+
         public final IBinder token;
         // Start time in CLOCK_BOOTTIME base.
         public final long startTime;
@@ -234,6 +245,7 @@
         // not to be affected by discontinuities created by RTC adjustments.
         public final long startTimeDebug;
         public final VibrationAttributes attrs;
+        public final long id;
         public final int uid;
         public final String opPkg;
         public final String reason;
@@ -248,6 +260,7 @@
                 VibrationAttributes attrs, int uid, String opPkg, String reason) {
             this.token = token;
             this.effect = effect;
+            this.id = mNextVibrationId.getAndIncrement();
             this.startTime = SystemClock.elapsedRealtime();
             this.startTimeDebug = System.currentTimeMillis();
             this.attrs = attrs;
@@ -268,19 +281,6 @@
             }
         }
 
-        /** Callback for when vibration is complete, to be called by native. */
-        @VisibleForTesting
-        public void onComplete() {
-            synchronized (mLock) {
-                if (this == mCurrentVibration) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "Vibration finished by callback, cleaning up");
-                    }
-                    doCancelVibrateLocked();
-                }
-            }
-        }
-
         public boolean hasTimeoutLongerThan(long millis) {
             final long duration = effect.getDuration();
             return duration >= 0 && duration > millis;
@@ -385,14 +385,14 @@
         mNativeWrapper = injector.getNativeWrapper();
         mH = injector.createHandler(Looper.myLooper());
 
-        long controllerPtr = mNativeWrapper.vibratorInit();
+        long nativeServicePtr = mNativeWrapper.vibratorInit(this::onVibrationComplete);
         long finalizerPtr = mNativeWrapper.vibratorGetFinalizer();
 
         if (finalizerPtr != 0) {
             NativeAllocationRegistry registry =
                     NativeAllocationRegistry.createMalloced(
                             VibratorService.class.getClassLoader(), finalizerPtr);
-            registry.registerNativeAllocation(this, controllerPtr);
+            registry.registerNativeAllocation(this, nativeServicePtr);
         }
 
         // Reset the hardware to a default state, in case this is a runtime
@@ -549,6 +549,19 @@
         }
     }
 
+    /** Callback for when vibration is complete, to be called by native. */
+    @VisibleForTesting
+    public void onVibrationComplete(long vibrationId) {
+        synchronized (mLock) {
+            if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) {
+                if (DEBUG) {
+                    Slog.d(TAG, "Vibration finished by callback, cleaning up");
+                }
+                doCancelVibrateLocked();
+            }
+        }
+    }
+
     @Override // Binder call
     public boolean hasVibrator() {
         return doVibratorExists();
@@ -1266,18 +1279,18 @@
         return mNativeWrapper.vibratorExists();
     }
 
-    /** Vibrates with native callback trigger for {@link Vibration#onComplete()}. */
+    /** Vibrates with native callback trigger for {@link #onVibrationComplete(long)}. */
     private void doVibratorOn(long millis, int amplitude, Vibration vib) {
-        doVibratorOn(millis, amplitude, vib.uid, vib.attrs, vib);
+        doVibratorOn(millis, amplitude, vib.uid, vib.attrs, vib.id);
     }
 
     /** Vibrates without native callback. */
     private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs) {
-        doVibratorOn(millis, amplitude, uid, attrs, /* vib= */ null);
+        doVibratorOn(millis, amplitude, uid, attrs, /* vibrationId= */ 0);
     }
 
     private void doVibratorOn(long millis, int amplitude, int uid, VibrationAttributes attrs,
-            @Nullable Vibration vib) {
+            long vibrationId) {
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
         try {
             synchronized (mInputDeviceVibrators) {
@@ -1299,7 +1312,7 @@
                     // Note: ordering is important here! Many haptic drivers will reset their
                     // amplitude when enabled, so we always have to enable first, then set the
                     // amplitude.
-                    mNativeWrapper.vibratorOn(millis, vib);
+                    mNativeWrapper.vibratorOn(millis, vibrationId);
                     doVibratorSetAmplitude(amplitude);
                 }
             }
@@ -1348,7 +1361,7 @@
             // Input devices don't support prebaked effect, so skip trying it with them.
             if (!usingInputDeviceVibrators) {
                 long duration = mNativeWrapper.vibratorPerformEffect(
-                        prebaked.getId(), prebaked.getEffectStrength(), vib);
+                        prebaked.getId(), prebaked.getEffectStrength(), vib.id);
                 if (duration > 0) {
                     noteVibratorOnLocked(vib.uid, duration);
                     return;
@@ -1395,7 +1408,7 @@
 
             PrimitiveEffect[] primitiveEffects =
                     composed.getPrimitiveEffects().toArray(new PrimitiveEffect[0]);
-            mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib);
+            mNativeWrapper.vibratorPerformComposedEffect(primitiveEffects, vib.id);
 
             // Composed effects don't actually give us an estimated duration, so we just guess here.
             noteVibratorOnLocked(vib.uid, 10 * primitiveEffects.length);
@@ -1726,20 +1739,20 @@
     @VisibleForTesting
     public static class NativeWrapper {
 
-        private long mNativeControllerPtr = 0;
+        private long mNativeServicePtr = 0;
 
         /** Checks if vibrator exists on device. */
         public boolean vibratorExists() {
-            return VibratorService.vibratorExists(mNativeControllerPtr);
+            return VibratorService.vibratorExists(mNativeServicePtr);
         }
 
         /**
          * Returns native pointer to newly created controller and initializes connection to vibrator
          * HAL service.
          */
-        public long vibratorInit() {
-            mNativeControllerPtr = VibratorService.vibratorInit();
-            return mNativeControllerPtr;
+        public long vibratorInit(OnCompleteListener listener) {
+            mNativeServicePtr = VibratorService.vibratorInit(listener);
+            return mNativeServicePtr;
         }
 
         /** Returns pointer to native finalizer function to be called by GC. */
@@ -1748,60 +1761,61 @@
         }
 
         /** Turns vibrator on for given time. */
-        public void vibratorOn(long milliseconds, @Nullable Vibration vibration) {
-            VibratorService.vibratorOn(mNativeControllerPtr, milliseconds, vibration);
+        public void vibratorOn(long milliseconds, long vibrationId) {
+            VibratorService.vibratorOn(mNativeServicePtr, milliseconds, vibrationId);
         }
 
         /** Turns vibrator off. */
         public void vibratorOff() {
-            VibratorService.vibratorOff(mNativeControllerPtr);
+            VibratorService.vibratorOff(mNativeServicePtr);
         }
 
         /** Sets the amplitude for the vibrator to run. */
         public void vibratorSetAmplitude(int amplitude) {
-            VibratorService.vibratorSetAmplitude(mNativeControllerPtr, amplitude);
+            VibratorService.vibratorSetAmplitude(mNativeServicePtr, amplitude);
         }
 
         /** Returns all predefined effects supported by the device vibrator. */
         public int[] vibratorGetSupportedEffects() {
-            return VibratorService.vibratorGetSupportedEffects(mNativeControllerPtr);
+            return VibratorService.vibratorGetSupportedEffects(mNativeServicePtr);
         }
 
         /** Returns all compose primitives supported by the device vibrator. */
         public int[] vibratorGetSupportedPrimitives() {
-            return VibratorService.vibratorGetSupportedPrimitives(mNativeControllerPtr);
+            return VibratorService.vibratorGetSupportedPrimitives(mNativeServicePtr);
         }
 
         /** Turns vibrator on to perform one of the supported effects. */
-        public long vibratorPerformEffect(long effect, long strength, Vibration vibration) {
+        public long vibratorPerformEffect(long effect, long strength, long vibrationId) {
             return VibratorService.vibratorPerformEffect(
-                    mNativeControllerPtr, effect, strength, vibration);
+                    mNativeServicePtr, effect, strength, vibrationId);
         }
 
         /** Turns vibrator on to perform one of the supported composed effects. */
         public void vibratorPerformComposedEffect(
-                VibrationEffect.Composition.PrimitiveEffect[] effect, Vibration vibration) {
-            VibratorService.vibratorPerformComposedEffect(mNativeControllerPtr, effect, vibration);
+                VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) {
+            VibratorService.vibratorPerformComposedEffect(mNativeServicePtr, effect,
+                    vibrationId);
         }
 
         /** Enabled the device vibrator to be controlled by another service. */
         public void vibratorSetExternalControl(boolean enabled) {
-            VibratorService.vibratorSetExternalControl(mNativeControllerPtr, enabled);
+            VibratorService.vibratorSetExternalControl(mNativeServicePtr, enabled);
         }
 
         /** Returns all capabilities of the device vibrator. */
         public long vibratorGetCapabilities() {
-            return VibratorService.vibratorGetCapabilities(mNativeControllerPtr);
+            return VibratorService.vibratorGetCapabilities(mNativeServicePtr);
         }
 
         /** Enable always-on vibration with given id and effect. */
         public void vibratorAlwaysOnEnable(long id, long effect, long strength) {
-            VibratorService.vibratorAlwaysOnEnable(mNativeControllerPtr, id, effect, strength);
+            VibratorService.vibratorAlwaysOnEnable(mNativeServicePtr, id, effect, strength);
         }
 
         /** Disable always-on vibration for given id. */
         public void vibratorAlwaysOnDisable(long id) {
-            VibratorService.vibratorAlwaysOnDisable(mNativeControllerPtr, id);
+            VibratorService.vibratorAlwaysOnDisable(mNativeServicePtr, id);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 6328cb60..c31d732 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -453,16 +453,16 @@
     }
 
     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, boolean hideFgNotification,
+            String callingPackage, @Nullable String callingFeatureId, final int userId)
             throws TransactionTooLargeException {
         return startServiceLocked(caller, service, resolvedType, callingPid, callingUid, fgRequired,
-                callingPackage, callingFeatureId, userId, false);
+                hideFgNotification, callingPackage, callingFeatureId, userId, false);
     }
 
     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, boolean hideFgNotification,
+            String callingPackage, @Nullable String callingFeatureId, final int userId,
             boolean allowBackgroundActivityStarts) throws TransactionTooLargeException {
         if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
                 + " type=" + resolvedType + " args=" + service.getExtras());
@@ -626,6 +626,7 @@
         r.startRequested = true;
         r.delayedStop = false;
         r.fgRequired = fgRequired;
+        r.hideFgNotification = hideFgNotification;
         r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                 service, neededGrants, callingUid));
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index e9539be..fede1d2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -89,6 +89,7 @@
     static final String KEY_MEMORY_INFO_THROTTLE_TIME = "memory_info_throttle_time";
     static final String KEY_TOP_TO_FGS_GRACE_DURATION = "top_to_fgs_grace_duration";
     static final String KEY_PENDINGINTENT_WARNING_THRESHOLD = "pendingintent_warning_threshold";
+    static final String KEY_MIN_CRASH_INTERVAL = "min_crash_interval";
 
     private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
     private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000;
@@ -122,6 +123,8 @@
     private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000;
     private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000;
     private static final int DEFAULT_PENDINGINTENT_WARNING_THRESHOLD = 2000;
+    private static final int DEFAULT_MIN_CRASH_INTERVAL = 2 * 60 * 1000;
+
 
     // Flag stored in the DeviceConfig API.
     /**
@@ -281,6 +284,12 @@
     // this long.
     public long TOP_TO_FGS_GRACE_DURATION = DEFAULT_TOP_TO_FGS_GRACE_DURATION;
 
+    /**
+     * The minimum time we allow between crashes, for us to consider this
+     * application to be bad and stop its services and reject broadcasts.
+     */
+    public static int MIN_CRASH_INTERVAL = DEFAULT_MIN_CRASH_INTERVAL;
+
     // Indicates whether the activity starts logging is enabled.
     // Controlled by Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED
     volatile boolean mFlagActivityStartsLoggingEnabled;
@@ -650,6 +659,8 @@
                     DEFAULT_MEMORY_INFO_THROTTLE_TIME);
             TOP_TO_FGS_GRACE_DURATION = mParser.getDurationMillis(KEY_TOP_TO_FGS_GRACE_DURATION,
                     DEFAULT_TOP_TO_FGS_GRACE_DURATION);
+            MIN_CRASH_INTERVAL = mParser.getInt(KEY_MIN_CRASH_INTERVAL,
+                    DEFAULT_MIN_CRASH_INTERVAL);
             PENDINGINTENT_WARNING_THRESHOLD = mParser.getInt(KEY_PENDINGINTENT_WARNING_THRESHOLD,
                     DEFAULT_PENDINGINTENT_WARNING_THRESHOLD);
 
@@ -866,6 +877,8 @@
         pw.println(MEMORY_INFO_THROTTLE_TIME);
         pw.print("  "); pw.print(KEY_TOP_TO_FGS_GRACE_DURATION); pw.print("=");
         pw.println(TOP_TO_FGS_GRACE_DURATION);
+        pw.print("  "); pw.print(KEY_MIN_CRASH_INTERVAL); pw.print("=");
+        pw.println(MIN_CRASH_INTERVAL);
         pw.print("  "); pw.print(KEY_IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES); pw.print("=");
         pw.println(Arrays.toString(IMPERCEPTIBLE_KILL_EXEMPT_PROC_STATES.toArray()));
         pw.print("  "); pw.print(KEY_IMPERCEPTIBLE_KILL_EXEMPT_PACKAGES); pw.print("=");
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3e600b7..2d803437 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -83,6 +83,7 @@
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BACKGROUND_CHECK;
@@ -117,7 +118,6 @@
 import static com.android.server.am.MemoryStatUtil.hasMemcg;
 import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
@@ -319,6 +319,7 @@
 import com.android.internal.os.ProcessCpuTracker;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.os.Zygote;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastPrintWriter;
@@ -1370,6 +1371,10 @@
 
     final Injector mInjector;
 
+    /** The package verifier app. */
+    private String mPackageVerifier;
+    private int mPackageVerifierUid = UserHandle.USER_NULL;
+
     static final class ProcessChangeItem {
         static final int CHANGE_ACTIVITIES = 1<<0;
         static final int CHANGE_FOREGROUND_SERVICES = 1<<1;
@@ -2246,6 +2251,18 @@
             if (phase == PHASE_SYSTEM_SERVICES_READY) {
                 mService.mBatteryStatsService.systemServicesReady();
                 mService.mServices.systemServicesReady();
+                mService.mPackageVerifier = ArrayUtils.firstOrNull(
+                        LocalServices.getService(PackageManagerInternal.class).getKnownPackageNames(
+                                PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM));
+                if (mService.mPackageVerifier != null) {
+                    try {
+                        mService.mPackageVerifierUid =
+                                getContext().getPackageManager().getPackageUid(
+                                        mService.mPackageVerifier, UserHandle.USER_SYSTEM);
+                    } catch (NameNotFoundException e) {
+                        Slog.wtf(TAG, "Package manager couldn't get package verifier uid", e);
+                    }
+                }
             } else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
                 mService.startBroadcastObservers();
             } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
@@ -4930,9 +4947,8 @@
                 notifyPackageUse(instr.mClass.getPackageName(),
                                  PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION);
             }
-            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc "
-                    + processName + " with config "
-                    + app.getWindowProcessController().getConfiguration());
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Binding proc %s with config %s",
+                    processName, app.getWindowProcessController().getConfiguration());
             ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
             app.compat = compatibilityInfoForPackage(appInfo);
 
@@ -13263,8 +13279,8 @@
 
     @Override
     public ComponentName startService(IApplicationThread caller, Intent service,
-            String resolvedType, boolean requireForeground, String callingPackage,
-            String callingFeatureId, int userId)
+            String resolvedType, boolean requireForeground, boolean hideForegroundNotification,
+            String callingPackage, String callingFeatureId, int userId)
             throws TransactionTooLargeException {
         enforceNotIsolatedCaller("startService");
         // Refuse possible leaked file descriptors
@@ -13276,17 +13292,27 @@
             throw new IllegalArgumentException("callingPackage cannot be null");
         }
 
+        final int callingUid = Binder.getCallingUid();
+        if (requireForeground && hideForegroundNotification) {
+            if (!UserHandle.isSameApp(callingUid, mPackageVerifierUid)
+                    || !callingPackage.equals(mPackageVerifier)) {
+                throw new IllegalArgumentException(
+                        "Only the package verifier can hide its foreground service notification");
+            }
+            Slog.i(TAG, "Foreground service notification hiding requested by " + callingPackage);
+        }
+
         if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
                 "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
         synchronized(this) {
             final int callingPid = Binder.getCallingPid();
-            final int callingUid = Binder.getCallingUid();
             final long origId = Binder.clearCallingIdentity();
             ComponentName res;
             try {
                 res = mServices.startServiceLocked(caller, service,
                         resolvedType, callingPid, callingUid,
-                        requireForeground, callingPackage, callingFeatureId, userId);
+                        requireForeground, hideForegroundNotification,
+                        callingPackage, callingFeatureId, userId);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -17670,7 +17696,7 @@
                 ComponentName res;
                 try {
                     res = mServices.startServiceLocked(null, service,
-                            resolvedType, -1, uid, fgRequired, callingPackage,
+                            resolvedType, -1, uid, fgRequired, false, callingPackage,
                             callingFeatureId, userId, allowBackgroundActivityStarts);
                 } finally {
                     Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 149e3ba..a512cca 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -654,7 +654,7 @@
         pw.println("Starting service: " + intent);
         pw.flush();
         ComponentName cn = mInterface.startService(null, intent, intent.getType(),
-                asForeground, SHELL_PACKAGE_NAME, null, mUserId);
+                asForeground, false, SHELL_PACKAGE_NAME, null, mUserId);
         if (cn == null) {
             err.println("Error: Not found; no service started.");
             return -1;
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 2e92ac0..5268359 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -716,7 +716,7 @@
             // back in the pending list.
             ServiceRecord sr = app.getRunningServiceAt(i);
             // If the service was restarted a while ago, then reset crash count, else increment it.
-            if (now > sr.restartTime + ProcessList.MIN_CRASH_INTERVAL) {
+            if (now > sr.restartTime + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
                 sr.crashCount = 1;
             } else {
                 sr.crashCount++;
@@ -729,7 +729,7 @@
             }
         }
 
-        if (crashTime != null && now < crashTime + ProcessList.MIN_CRASH_INTERVAL) {
+        if (crashTime != null && now < crashTime + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
             // The process crashed again very quickly. If it was a bound foreground service, let's
             // try to restart again in a while, otherwise the process loses!
             Slog.w(TAG, "Process " + app.processName
@@ -771,7 +771,7 @@
                 data.taskId = affectedTaskId;
             }
             if (data != null && crashTimePersistent != null
-                    && now < crashTimePersistent + ProcessList.MIN_CRASH_INTERVAL) {
+                    && now < crashTimePersistent + ActivityManagerConstants.MIN_CRASH_INTERVAL) {
                 data.repeating = true;
             }
         }
@@ -853,7 +853,7 @@
                     mAppsNotReportingCrashes.contains(proc.info.packageName);
             final long now = SystemClock.uptimeMillis();
             final boolean shouldThottle = crashShowErrorTime != null
-                    && now < crashShowErrorTime + ProcessList.MIN_CRASH_INTERVAL;
+                    && now < crashShowErrorTime + ActivityManagerConstants.MIN_CRASH_INTERVAL;
             if ((mService.mAtmInternal.canShowErrorDialogs() || showBackground)
                     && !crashSilenced && !shouldThottle
                     && (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 4673ecd..76089f8 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -154,10 +154,6 @@
     static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
             "persist.sys.vold_app_data_isolation_enabled";
 
-    // The minimum time we allow between crashes, for us to consider this
-    // application to be bad and stop and its services and reject broadcasts.
-    static final int MIN_CRASH_INTERVAL = 60 * 1000;
-
     // OOM adjustments for processes in various states:
 
     // Uninitialized value for any major or minor adj fields
@@ -4070,7 +4066,8 @@
         boolean enqueueLocked(ProcessRecord app, String reason, int requester) {
             // Throttle the killing request for potential bad app to avoid cpu thrashing
             Long last = app.isolated ? null : mLastProcessKillTimes.get(app.processName, app.uid);
-            if (last != null && SystemClock.uptimeMillis() < last + MIN_CRASH_INTERVAL) {
+            if ((last != null) && (SystemClock.uptimeMillis()
+                    < (last + ActivityManagerConstants.MIN_CRASH_INTERVAL))) {
                 return false;
             }
 
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 66677b67..5b12c8c 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -104,6 +104,7 @@
     boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT?
     boolean delayed;        // are we waiting to start this service in the background?
     boolean fgRequired;     // is the service required to go foreground after starting?
+    boolean hideFgNotification; // Hide the fg service notification
     boolean fgWaiting;      // is a timeout for going foreground already scheduled?
     boolean isForeground;   // is service currently in foreground mode?
     int foregroundId;       // Notification ID of last foreground req.
@@ -836,6 +837,9 @@
     }
 
     public void postNotification() {
+        if (hideFgNotification) {
+            return;
+        }
         final int appUid = appInfo.uid;
         final int appPid = app.pid;
         if (foregroundId != 0 && foregroundNoti != null) {
@@ -928,7 +932,7 @@
                         }
                         if (localForegroundNoti.getSmallIcon() == null) {
                             // Notifications whose icon is 0 are defined to not show
-                            // a notification, silently ignoring it.  We don't want to
+                            // a notification.  We don't want to
                             // just ignore it, we want to prevent the service from
                             // being foreground.
                             throw new RuntimeException("invalid service notification: "
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 06ef58f..5447605 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -500,8 +500,8 @@
         sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType);
     }
 
-    /*package*/ void postSetModeOwnerPid(int pid) {
-        sendIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid);
+    /*package*/ void postSetModeOwnerPid(int pid, int mode) {
+        sendIIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid, mode);
     }
 
     /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) {
@@ -977,7 +977,9 @@
                         synchronized (mDeviceStateLock) {
                             if (mModeOwnerPid != msg.arg1) {
                                 mModeOwnerPid = msg.arg1;
-                                updateSpeakerphoneOn("setNewModeOwner");
+                                if (msg.arg2 != AudioSystem.MODE_RINGTONE) {
+                                    updateSpeakerphoneOn("setNewModeOwner");
+                                }
                                 if (mModeOwnerPid != 0) {
                                     mBtHelper.disconnectBluetoothSco(mModeOwnerPid);
                                 }
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0a179e8..3f29eb5 100755
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -285,6 +285,8 @@
     private static final int MSG_PLAYBACK_CONFIG_CHANGE = 29;
     private static final int MSG_BROADCAST_MICROPHONE_MUTE = 30;
     private static final int MSG_CHECK_MODE_FOR_UID = 31;
+    private static final int MSG_STREAM_DEVICES_CHANGED = 32;
+    private static final int MSG_UPDATE_VOLUME_STATES_FOR_DEVICE = 33;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
@@ -1283,7 +1285,6 @@
             }
 
             if (isPlatformTelevision()) {
-                checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI, caller);
                 synchronized (mHdmiClientLock) {
                     if (mHdmiManager != null && mHdmiPlaybackClient != null) {
                         updateHdmiCecSinkLocked(mHdmiCecSink | false);
@@ -1303,22 +1304,71 @@
         }
     }
 
-    private void checkAddAllFixedVolumeDevices(int device, String caller) {
-        final int numStreamTypes = AudioSystem.getNumStreamTypes();
-        for (int streamType = 0; streamType < numStreamTypes; streamType++) {
-            if (!mStreamStates[streamType].hasIndexForDevice(device)) {
-                // set the default value, if device is affected by a full/fix/abs volume rule, it
-                // will taken into account in checkFixedVolumeDevices()
-                mStreamStates[streamType].setIndex(
-                        mStreamStates[mStreamVolumeAlias[streamType]]
-                                .getIndex(AudioSystem.DEVICE_OUT_DEFAULT),
-                        device, caller, true /*hasModifyAudioSettings*/);
-            }
-            mStreamStates[streamType].checkFixedVolumeDevices();
+    /**
+     * Asynchronously update volume states for the given device.
+     *
+     * @param device a single audio device, ensure that this is not a devices bitmask
+     * @param caller caller of this method
+     */
+    private void postUpdateVolumeStatesForAudioDevice(int device, String caller) {
+        sendMsg(mAudioHandler,
+                MSG_UPDATE_VOLUME_STATES_FOR_DEVICE,
+                SENDMSG_QUEUE, device /*arg1*/, 0 /*arg2*/, caller /*obj*/,
+                0 /*delay*/);
+    }
 
-            // Unmute streams if device is full volume
-            if (mFullVolumeDevices.contains(device)) {
-                mStreamStates[streamType].mute(false);
+    /**
+     * Update volume states for the given device.
+     *
+     * This will initialize the volume index if no volume index is available.
+     * If the device is the currently routed device, fixed/full volume policies will be applied.
+     *
+     * @param device a single audio device, ensure that this is not a devices bitmask
+     * @param caller caller of this method
+     */
+    private void onUpdateVolumeStatesForAudioDevice(int device, String caller) {
+        final int numStreamTypes = AudioSystem.getNumStreamTypes();
+        synchronized (mSettingsLock) {
+            synchronized (VolumeStreamState.class) {
+                for (int streamType = 0; streamType < numStreamTypes; streamType++) {
+                    updateVolumeStates(device, streamType, caller);
+                }
+            }
+        }
+    }
+
+    /**
+     * Update volume states for the given device and given stream.
+     *
+     * This will initialize the volume index if no volume index is available.
+     * If the device is the currently routed device, fixed/full volume policies will be applied.
+     *
+     * @param device a single audio device, ensure that this is not a devices bitmask
+     * @param streamType streamType to be updated
+     * @param caller caller of this method
+     */
+    private void updateVolumeStates(int device, int streamType, String caller) {
+        if (!mStreamStates[streamType].hasIndexForDevice(device)) {
+            // set the default value, if device is affected by a full/fix/abs volume rule, it
+            // will taken into account in checkFixedVolumeDevices()
+            mStreamStates[streamType].setIndex(
+                    mStreamStates[mStreamVolumeAlias[streamType]]
+                            .getIndex(AudioSystem.DEVICE_OUT_DEFAULT),
+                    device, caller, true /*hasModifyAudioSettings*/);
+        }
+
+        // Check if device to be updated is routed for the given audio stream
+        List<AudioDeviceAttributes> devicesForAttributes = getDevicesForAttributesInt(
+                new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build());
+        for (AudioDeviceAttributes deviceAttributes : devicesForAttributes) {
+            if (deviceAttributes.getType() == AudioDeviceInfo.convertInternalDeviceToDeviceType(
+                    device)) {
+                mStreamStates[streamType].checkFixedVolumeDevices();
+
+                // Unmute streams if required and device is full volume
+                if (isStreamMute(streamType) && mFullVolumeDevices.contains(device)) {
+                    mStreamStates[streamType].mute(false);
+                }
             }
         }
     }
@@ -1868,8 +1918,13 @@
     /** @see AudioManager#getDevicesForAttributes(AudioAttributes) */
     public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
             @NonNull AudioAttributes attributes) {
-        Objects.requireNonNull(attributes);
         enforceModifyAudioRoutingPermission();
+        return getDevicesForAttributesInt(attributes);
+    }
+
+    protected @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesInt(
+            @NonNull AudioAttributes attributes) {
+        Objects.requireNonNull(attributes);
         return AudioSystem.getDevicesForAttributes(attributes);
     }
 
@@ -3692,13 +3747,15 @@
         private final IBinder mCb; // To be notified of client's death
         private final int mPid;
         private final int mUid;
-        private String mPackage;
+        private final boolean mIsPrivileged;
+        private final String mPackage;
         private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
 
-        SetModeDeathHandler(IBinder cb, int pid, int uid, String caller) {
+        SetModeDeathHandler(IBinder cb, int pid, int uid, boolean isPrivileged, String caller) {
             mCb = cb;
             mPid = pid;
             mUid = uid;
+            mIsPrivileged = isPrivileged;
             mPackage = caller;
         }
 
@@ -3710,12 +3767,13 @@
                 if (index < 0) {
                     Log.w(TAG, "unregistered setMode() client died");
                 } else {
-                    newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, mUid, TAG);
+                    newModeOwnerPid = setModeInt(
+                            AudioSystem.MODE_NORMAL, mCb, mPid, mUid, mIsPrivileged, TAG);
                 }
             }
             // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
             // SCO connections not started by the application changing the mode when pid changes
-            mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid);
+            mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, AudioService.this.getMode());
         }
 
         public int getPid() {
@@ -3741,6 +3799,10 @@
         public String getPackage() {
             return mPackage;
         }
+
+        public boolean isPrivileged() {
+            return mIsPrivileged;
+        }
     }
 
     /** @see AudioManager#setMode(int) */
@@ -3792,18 +3854,19 @@
                         + " without permission or being mode owner");
                 return;
             }
-            newModeOwnerPid = setModeInt(
-                mode, cb, callingPid, Binder.getCallingUid(), callingPackage);
+            newModeOwnerPid = setModeInt(mode, cb, callingPid, Binder.getCallingUid(),
+                    hasModifyPhoneStatePermission, callingPackage);
         }
         // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
         // SCO connections not started by the application changing the mode when pid changes
-        mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid);
+        mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid, getMode());
     }
 
     // setModeInt() returns a valid PID if the audio mode was successfully set to
     // any mode other than NORMAL.
     @GuardedBy("mDeviceBroker.mSetModeLock")
-    private int setModeInt(int mode, IBinder cb, int pid, int uid, String caller) {
+    private int setModeInt(
+            int mode, IBinder cb, int pid, int uid, boolean isPrivileged, String caller) {
         if (DEBUG_MODE) {
             Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid
                     + ", uid=" + uid + ", caller=" + caller + ")");
@@ -3855,7 +3918,7 @@
                 }
             } else {
                 if (hdlr == null) {
-                    hdlr = new SetModeDeathHandler(cb, pid, uid, caller);
+                    hdlr = new SetModeDeathHandler(cb, pid, uid, isPrivileged, caller);
                 }
                 // Register for client death notification
                 try {
@@ -3914,7 +3977,8 @@
             // change of mode may require volume to be re-applied on some devices
             updateAbsVolumeMultiModeDevices(oldMode, actualMode);
 
-            if (actualMode == AudioSystem.MODE_IN_COMMUNICATION) {
+            if (actualMode == AudioSystem.MODE_IN_COMMUNICATION
+                    && !hdlr.isPrivileged()) {
                 sendMsg(mAudioHandler,
                         MSG_CHECK_MODE_FOR_UID,
                         SENDMSG_QUEUE,
@@ -4912,11 +4976,21 @@
         }
     }
 
-    private void observeDevicesForStreams(int skipStream) {
-        synchronized (VolumeStreamState.class) {
-            for (int stream = 0; stream < mStreamStates.length; stream++) {
-                if (stream != skipStream) {
-                    mStreamStates[stream].observeDevicesForStream_syncVSS(false /*checkOthers*/);
+    private void onObserveDevicesForAllStreams(int skipStream) {
+        synchronized (mSettingsLock) {
+            synchronized (VolumeStreamState.class) {
+                for (int stream = 0; stream < mStreamStates.length; stream++) {
+                    if (stream != skipStream) {
+                        int devices = mStreamStates[stream].observeDevicesForStream_syncVSS(
+                                false /*checkOthers*/);
+
+                        Set<Integer> devicesSet = AudioSystem.generateAudioDeviceTypesSet(devices);
+                        for (Integer device : devicesSet) {
+                            // Update volume states for devices routed for the stream
+                            updateVolumeStates(device, stream,
+                                    "AudioService#onObserveDevicesForAllStreams");
+                        }
+                    }
                 }
             }
         }
@@ -4925,14 +4999,16 @@
     /** only public for mocking/spying, do not call outside of AudioService */
     @VisibleForTesting
     public void postObserveDevicesForAllStreams() {
-        sendMsg(mAudioHandler,
-                MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS,
-                SENDMSG_QUEUE, 0 /*arg1*/, 0 /*arg2*/, null /*obj*/,
-                0 /*delay*/);
+        postObserveDevicesForAllStreams(-1);
     }
 
-    private void onObserveDevicesForAllStreams() {
-        observeDevicesForStreams(-1);
+    /** only public for mocking/spying, do not call outside of AudioService */
+    @VisibleForTesting
+    public void postObserveDevicesForAllStreams(int skipStream) {
+        sendMsg(mAudioHandler,
+                MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS,
+                SENDMSG_QUEUE, skipStream /*arg1*/, 0 /*arg2*/, null /*obj*/,
+                0 /*delay*/);
     }
 
     /**
@@ -4985,7 +5061,8 @@
                       + Integer.toHexString(audioSystemDeviceOut) + " from:" + caller));
         // make sure we have a volume entry for this device, and that volume is updated according
         // to volume behavior
-        checkAddAllFixedVolumeDevices(audioSystemDeviceOut, "setDeviceVolumeBehavior:" + caller);
+        postUpdateVolumeStatesForAudioDevice(audioSystemDeviceOut,
+                "setDeviceVolumeBehavior:" + caller);
     }
 
     /**
@@ -5616,6 +5693,7 @@
             }
         }
 
+        @GuardedBy("VolumeStreamState.class")
         public int observeDevicesForStream_syncVSS(boolean checkOthers) {
             if (!mSystemServer.isPrivileged()) {
                 return AudioSystem.DEVICE_NONE;
@@ -5628,15 +5706,19 @@
             mObservedDevices = devices;
             if (checkOthers) {
                 // one stream's devices have changed, check the others
-                observeDevicesForStreams(mStreamType);
+                postObserveDevicesForAllStreams(mStreamType);
             }
             // log base stream changes to the event log
             if (mStreamVolumeAlias[mStreamType] == mStreamType) {
                 EventLogTags.writeStreamDevicesChanged(mStreamType, prevDevices, devices);
             }
-            sendBroadcastToAll(mStreamDevicesChanged
-                    .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, prevDevices)
-                    .putExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, devices));
+            // send STREAM_DEVICES_CHANGED_ACTION on the message handler so it is scheduled after
+            // the postObserveDevicesForStreams is handled
+            sendMsg(mAudioHandler,
+                    MSG_STREAM_DEVICES_CHANGED,
+                    SENDMSG_QUEUE, prevDevices /*arg1*/, devices /*arg2*/,
+                    // ok to send reference to this object, it is final
+                    mStreamDevicesChanged /*obj*/, 0 /*delay*/);
             return devices;
         }
 
@@ -6408,7 +6490,7 @@
                     break;
 
                 case MSG_OBSERVE_DEVICES_FOR_ALL_STREAMS:
-                    onObserveDevicesForAllStreams();
+                    onObserveDevicesForAllStreams(/*skipStream*/ msg.arg1);
                     break;
 
                 case MSG_HDMI_VOLUME_CHECK:
@@ -6446,11 +6528,21 @@
                                     CHECK_MODE_FOR_UID_PERIOD_MS);
                             break;
                         }
-                        // For now just log the fact that an app is hogging the audio mode.
-                        // TODO(b/160260850): remove abusive app from audio mode stack.
+                        setModeInt(AudioSystem.MODE_NORMAL, h.getBinder(), h.getPid(), h.getUid(),
+                                h.isPrivileged(), "MSG_CHECK_MODE_FOR_UID");
                         mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid()));
                     }
                     break;
+
+                case MSG_STREAM_DEVICES_CHANGED:
+                    sendBroadcastToAll(((Intent) msg.obj)
+                            .putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_DEVICES, msg.arg1)
+                            .putExtra(AudioManager.EXTRA_VOLUME_STREAM_DEVICES, msg.arg2));
+                    break;
+
+                case MSG_UPDATE_VOLUME_STATES_FOR_DEVICE:
+                    onUpdateVolumeStatesForAudioDevice(msg.arg1, (String) msg.obj);
+                    break;
             }
         }
     }
@@ -7207,10 +7299,9 @@
                 // HDMI output
                 removeAudioSystemDeviceOutFromFullVolumeDevices(AudioSystem.DEVICE_OUT_HDMI);
             }
+            postUpdateVolumeStatesForAudioDevice(AudioSystem.DEVICE_OUT_HDMI,
+                    "HdmiPlaybackClient.DisplayStatusCallback");
         }
-
-        checkAddAllFixedVolumeDevices(AudioSystem.DEVICE_OUT_HDMI,
-                "HdmiPlaybackClient.DisplayStatusCallback");
     }
 
     private class MyHdmiControlStatusChangeListenerCallback
diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
index 7f9b3c9..4c63eb4 100644
--- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java
+++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java
@@ -21,23 +21,14 @@
 import static android.Manifest.permission.INTERNET;
 import static android.Manifest.permission.NETWORK_STACK;
 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
-import static android.net.INetd.PERMISSION_INTERNET;
-import static android.net.INetd.PERMISSION_NETWORK;
-import static android.net.INetd.PERMISSION_NONE;
-import static android.net.INetd.PERMISSION_SYSTEM;
-import static android.net.INetd.PERMISSION_UNINSTALLED;
-import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
 
-import static com.android.internal.util.ArrayUtils.convertToIntArray;
-
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -56,9 +47,11 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.SparseIntArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
@@ -72,6 +65,7 @@
 import java.util.Map.Entry;
 import java.util.Set;
 
+
 /**
  * A utility class to inform Netd of UID permisisons.
  * Does a mass update at boot and then monitors for app install/remove.
@@ -120,51 +114,9 @@
         public int getDeviceFirstSdkInt() {
             return Build.VERSION.FIRST_SDK_INT;
         }
-
-        /**
-         * Check whether given uid has specific permission.
-         */
-        public int uidPermission(@NonNull final String permission, final int uid) {
-            return ActivityManager.checkUidPermission(permission, uid);
-        }
     }
 
-    /**
-     * A data class to store each uid Netd permission information. Netd permissions includes
-     * PERMISSION_NETWORK, PERMISSION_SYSTEM, PERMISSION_INTERNET, PERMISSION_UPDATE_DEVICE_STATS
-     * and OR'd with the others. Default permission is PERMISSION_NONE and PERMISSION_UNINSTALLED
-     * will be set if all packages are removed from the uid.
-     */
-    public static class UidNetdPermissionInfo {
-        private final int mNetdPermissions;
-
-        UidNetdPermissionInfo() {
-            this(PERMISSION_NONE);
-        }
-
-        UidNetdPermissionInfo(int permissions) {
-            mNetdPermissions = permissions;
-        }
-
-        /** Plus given permissions and return new UidNetdPermissionInfo instance. */
-        public UidNetdPermissionInfo plusNetdPermissions(int permissions) {
-            return new UidNetdPermissionInfo(mNetdPermissions | permissions);
-        }
-
-        /** Return whether package is uninstalled. */
-        public boolean isPackageUninstalled() {
-            return mNetdPermissions == PERMISSION_UNINSTALLED;
-        }
-
-        /** Check that uid has given permissions */
-        public boolean hasNetdPermissions(final int permissions) {
-            if (isPackageUninstalled()) return false;
-            if (permissions == PERMISSION_NONE) return true;
-            return (mNetdPermissions & permissions) == permissions;
-        }
-    }
-
-    public PermissionMonitor(Context context, INetd netd) {
+    public PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd) {
         this(context, netd, new Dependencies());
     }
 
@@ -195,7 +147,7 @@
             return;
         }
 
-        final SparseArray<UidNetdPermissionInfo> netdPermsUids = new SparseArray<>();
+        SparseIntArray netdPermsUids = new SparseIntArray();
 
         for (PackageInfo app : apps) {
             int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
@@ -204,9 +156,8 @@
             }
             mAllApps.add(UserHandle.getAppId(uid));
 
-            final boolean isNetwork = hasPermission(CHANGE_NETWORK_STATE, uid);
-            final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(uid)
-                    || isCarryoverPackage(app.applicationInfo);
+            boolean isNetwork = hasNetworkPermission(app);
+            boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
 
             if (isNetwork || hasRestrictedPermission) {
                 Boolean permission = mApps.get(uid);
@@ -217,16 +168,13 @@
                 }
             }
 
-            // Skip already checked uid.
-            if (netdPermsUids.get(uid) != null) continue;
-
             //TODO: unify the management of the permissions into one codepath.
-            final UidNetdPermissionInfo permInfo =
-                    new UidNetdPermissionInfo(getNetdPermissionMask(uid));
-            netdPermsUids.put(uid, permInfo);
+            int otherNetdPerms = getNetdPermissionMask(app.requestedPermissions,
+                    app.requestedPermissionsFlags);
+            netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms);
         }
 
-        List<UserInfo> users = mUserManager.getUsers(true);  // exclude dying users
+        List<UserInfo> users = mUserManager.getAliveUsers();
         if (users != null) {
             for (UserInfo user : users) {
                 mUsers.add(user.id);
@@ -238,17 +186,15 @@
         for (int i = 0; i < systemPermission.size(); i++) {
             ArraySet<String> perms = systemPermission.valueAt(i);
             int uid = systemPermission.keyAt(i);
-            int netdPermission = PERMISSION_NONE;
+            int netdPermission = 0;
             // Get the uids of native services that have UPDATE_DEVICE_STATS or INTERNET permission.
             if (perms != null) {
                 netdPermission |= perms.contains(UPDATE_DEVICE_STATS)
-                        ? PERMISSION_UPDATE_DEVICE_STATS : 0;
-                netdPermission |= perms.contains(INTERNET) ? PERMISSION_INTERNET : 0;
+                        ? INetd.PERMISSION_UPDATE_DEVICE_STATS : 0;
+                netdPermission |= perms.contains(INTERNET)
+                        ? INetd.PERMISSION_INTERNET : 0;
             }
-            final UidNetdPermissionInfo permInfo = netdPermsUids.get(uid);
-            netdPermsUids.put(uid, permInfo != null
-                    ? permInfo.plusNetdPermissions(netdPermission)
-                    : new UidNetdPermissionInfo(netdPermission));
+            netdPermsUids.put(uid, netdPermsUids.get(uid) | netdPermission);
         }
         log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
         update(mUsers, mApps, true);
@@ -261,34 +207,48 @@
     }
 
     @VisibleForTesting
-    boolean hasPermission(@NonNull final String permission, final int uid) {
-        return mDeps.uidPermission(permission, uid) == PackageManager.PERMISSION_GRANTED;
+    boolean hasPermission(@NonNull final PackageInfo app, @NonNull final String permission) {
+        if (app.requestedPermissions == null || app.requestedPermissionsFlags == null) {
+            return false;
+        }
+        final int index = ArrayUtils.indexOf(app.requestedPermissions, permission);
+        if (index < 0 || index >= app.requestedPermissionsFlags.length) return false;
+        return (app.requestedPermissionsFlags[index] & REQUESTED_PERMISSION_GRANTED) != 0;
     }
 
     @VisibleForTesting
-    // TODO : remove this check in the future(b/162295056). All apps should just request the
-    // appropriate permission for their use case since android Q.
-    boolean isCarryoverPackage(@Nullable final ApplicationInfo appInfo) {
-        if (appInfo == null) return false;
-        return (appInfo.targetSdkVersion < VERSION_Q && isVendorApp(appInfo))
-                // Backward compatibility for b/114245686, on devices that launched before Q daemons
-                // and apps running as the system UID are exempted from this check.
-                || (appInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q);
+    boolean hasNetworkPermission(@NonNull final PackageInfo app) {
+        return hasPermission(app, CHANGE_NETWORK_STATE);
     }
 
     @VisibleForTesting
-    boolean hasRestrictedNetworkPermission(final int uid) {
-        return hasPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, uid)
-                || hasPermission(PERMISSION_MAINLINE_NETWORK_STACK, uid)
-                || hasPermission(NETWORK_STACK, uid);
+    boolean hasRestrictedNetworkPermission(@NonNull final PackageInfo app) {
+        // TODO : remove this check in the future(b/31479477). All apps should just
+        // request the appropriate permission for their use case since android Q.
+        if (app.applicationInfo != null) {
+            // Backward compatibility for b/114245686, on devices that launched before Q daemons
+            // and apps running as the system UID are exempted from this check.
+            if (app.applicationInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q) {
+                return true;
+            }
+
+            if (app.applicationInfo.targetSdkVersion < VERSION_Q
+                    && isVendorApp(app.applicationInfo)) {
+                return true;
+            }
+        }
+
+        return hasPermission(app, PERMISSION_MAINLINE_NETWORK_STACK)
+                || hasPermission(app, NETWORK_STACK)
+                || hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
     }
 
     /** Returns whether the given uid has using background network permission. */
     public synchronized boolean hasUseBackgroundNetworksPermission(final int uid) {
         // Apps with any of the CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_INTERNAL or
         // CONNECTIVITY_USE_RESTRICTED_NETWORKS permission has the permission to use background
-        // networks. mApps contains the result of checks for both CHANGE_NETWORK_STATE permission
-        // and hasRestrictedNetworkPermission. If uid is in the mApps list that means uid has one of
+        // networks. mApps contains the result of checks for both hasNetworkPermission and
+        // hasRestrictedNetworkPermission. If uid is in the mApps list that means uid has one of
         // permissions at least.
         return mApps.containsKey(uid);
     }
@@ -313,11 +273,11 @@
         }
         try {
             if (add) {
-                mNetd.networkSetPermissionForUser(PERMISSION_NETWORK, convertToIntArray(network));
-                mNetd.networkSetPermissionForUser(PERMISSION_SYSTEM, convertToIntArray(system));
+                mNetd.networkSetPermissionForUser(INetd.PERMISSION_NETWORK, toIntArray(network));
+                mNetd.networkSetPermissionForUser(INetd.PERMISSION_SYSTEM, toIntArray(system));
             } else {
-                mNetd.networkClearPermissionForUser(convertToIntArray(network));
-                mNetd.networkClearPermissionForUser(convertToIntArray(system));
+                mNetd.networkClearPermissionForUser(toIntArray(network));
+                mNetd.networkClearPermissionForUser(toIntArray(system));
             }
         } catch (RemoteException e) {
             loge("Exception when updating permissions: " + e);
@@ -363,15 +323,14 @@
     }
 
     @VisibleForTesting
-    protected Boolean highestPermissionForUid(Boolean currentPermission, String name, int uid) {
+    protected Boolean highestPermissionForUid(Boolean currentPermission, String name) {
         if (currentPermission == SYSTEM) {
             return currentPermission;
         }
         try {
             final PackageInfo app = mPackageManager.getPackageInfo(name, GET_PERMISSIONS);
-            final boolean isNetwork = hasPermission(CHANGE_NETWORK_STATE, uid);
-            final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(uid)
-                    || isCarryoverPackage(app.applicationInfo);
+            final boolean isNetwork = hasNetworkPermission(app);
+            final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
             if (isNetwork || hasRestrictedPermission) {
                 currentPermission = hasRestrictedPermission;
             }
@@ -382,15 +341,24 @@
         return currentPermission;
     }
 
-    private UidNetdPermissionInfo getPermissionForUid(final int uid) {
+    private int getPermissionForUid(final int uid) {
+        int permission = INetd.PERMISSION_NONE;
         // Check all the packages for this UID. The UID has the permission if any of the
         // packages in it has the permission.
         final String[] packages = mPackageManager.getPackagesForUid(uid);
-        if (packages == null || packages.length <= 0) {
+        if (packages != null && packages.length > 0) {
+            for (String name : packages) {
+                final PackageInfo app = getPackageInfo(name);
+                if (app != null && app.requestedPermissions != null) {
+                    permission |= getNetdPermissionMask(app.requestedPermissions,
+                            app.requestedPermissionsFlags);
+                }
+            }
+        } else {
             // The last package of this uid is removed from device. Clean the package up.
-            return new UidNetdPermissionInfo(PERMISSION_UNINSTALLED);
+            permission = INetd.PERMISSION_UNINSTALLED;
         }
-        return new UidNetdPermissionInfo(getNetdPermissionMask(uid));
+        return permission;
     }
 
     /**
@@ -407,7 +375,7 @@
 
         // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
         // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
-        final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName, uid);
+        final Boolean permission = highestPermissionForUid(mApps.get(uid), packageName);
         if (permission != mApps.get(uid)) {
             mApps.put(uid, permission);
 
@@ -463,7 +431,7 @@
         String[] packages = mPackageManager.getPackagesForUid(uid);
         if (packages != null && packages.length > 0) {
             for (String name : packages) {
-                permission = highestPermissionForUid(permission, name, uid);
+                permission = highestPermissionForUid(permission, name);
                 if (permission == SYSTEM) {
                     // An app with this UID still has the SYSTEM permission.
                     // Therefore, this UID must already have the SYSTEM permission.
@@ -499,13 +467,19 @@
         sendPackagePermissionsForUid(uid, getPermissionForUid(uid));
     }
 
-    private int getNetdPermissionMask(final int uid) {
-        int permissions = PERMISSION_NONE;
-        if (hasPermission(INTERNET, uid)) {
-            permissions |= PERMISSION_INTERNET;
-        }
-        if (hasPermission(UPDATE_DEVICE_STATS, uid)) {
-            permissions |= PERMISSION_UPDATE_DEVICE_STATS;
+    private static int getNetdPermissionMask(String[] requestedPermissions,
+                                             int[] requestedPermissionsFlags) {
+        int permissions = 0;
+        if (requestedPermissions == null || requestedPermissionsFlags == null) return permissions;
+        for (int i = 0; i < requestedPermissions.length; i++) {
+            if (requestedPermissions[i].equals(INTERNET)
+                    && ((requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED) != 0)) {
+                permissions |= INetd.PERMISSION_INTERNET;
+            }
+            if (requestedPermissions[i].equals(UPDATE_DEVICE_STATS)
+                    && ((requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED) != 0)) {
+                permissions |= INetd.PERMISSION_UPDATE_DEVICE_STATS;
+            }
         }
         return permissions;
     }
@@ -640,28 +614,28 @@
      * permission information to netd.
      *
      * @param uid the app uid of the package installed
-     * @param permissionInfo the permission info of given uid.
+     * @param permissions the permissions the app requested and netd cares about.
      *
      * @hide
      */
     @VisibleForTesting
-    void sendPackagePermissionsForUid(int uid, UidNetdPermissionInfo permissionInfo) {
-        final SparseArray<UidNetdPermissionInfo> uidsPermInfo = new SparseArray<>();
-        uidsPermInfo.put(uid, permissionInfo);
-        sendPackagePermissionsToNetd(uidsPermInfo);
+    void sendPackagePermissionsForUid(int uid, int permissions) {
+        SparseIntArray netdPermissionsAppIds = new SparseIntArray();
+        netdPermissionsAppIds.put(uid, permissions);
+        sendPackagePermissionsToNetd(netdPermissionsAppIds);
     }
 
     /**
      * Called by packageManagerService to send IPC to netd. Grant or revoke the INTERNET
      * and/or UPDATE_DEVICE_STATS permission of the uids in array.
      *
-     * @param uidsPermInfo permission info array generated from each uid. If the uid permission is
-     *                     PERMISSION_NONE or PERMISSION_UNINSTALLED, revoke all permissions of that
-     *                     uid.
+     * @param netdPermissionsAppIds integer pairs of uids and the permission granted to it. If the
+     * permission is 0, revoke all permissions of that uid.
+     *
      * @hide
      */
     @VisibleForTesting
-    void sendPackagePermissionsToNetd(final SparseArray<UidNetdPermissionInfo> uidsPermInfo) {
+    void sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds) {
         if (mNetd == null) {
             Log.e(TAG, "Failed to get the netd service");
             return;
@@ -671,44 +645,50 @@
         ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
         ArrayList<Integer> noPermissionAppIds = new ArrayList<>();
         ArrayList<Integer> uninstalledAppIds = new ArrayList<>();
-        for (int i = 0; i < uidsPermInfo.size(); i++) {
-            final int uid = uidsPermInfo.keyAt(i);
-            final UidNetdPermissionInfo permInfo = uidsPermInfo.valueAt(i);
-            if (permInfo.hasNetdPermissions(
-                    PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS)) {
-                allPermissionAppIds.add(uid);
-            } else if (permInfo.hasNetdPermissions(PERMISSION_INTERNET)) {
-                internetPermissionAppIds.add(uid);
-            } else if (permInfo.hasNetdPermissions(PERMISSION_UPDATE_DEVICE_STATS)) {
-                updateStatsPermissionAppIds.add(uid);
-            } else if (permInfo.isPackageUninstalled()) {
-                uninstalledAppIds.add(uid);
-            } else {
-                noPermissionAppIds.add(uid);
+        for (int i = 0; i < netdPermissionsAppIds.size(); i++) {
+            int permissions = netdPermissionsAppIds.valueAt(i);
+            switch(permissions) {
+                case (INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS):
+                    allPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+                    break;
+                case INetd.PERMISSION_INTERNET:
+                    internetPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+                    break;
+                case INetd.PERMISSION_UPDATE_DEVICE_STATS:
+                    updateStatsPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+                    break;
+                case INetd.PERMISSION_NONE:
+                    noPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
+                    break;
+                case INetd.PERMISSION_UNINSTALLED:
+                    uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
+                default:
+                    Log.e(TAG, "unknown permission type: " + permissions + "for uid: "
+                            + netdPermissionsAppIds.keyAt(i));
             }
         }
         try {
             // TODO: add a lock inside netd to protect IPC trafficSetNetPermForUids()
             if (allPermissionAppIds.size() != 0) {
                 mNetd.trafficSetNetPermForUids(
-                        PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS,
-                        convertToIntArray(allPermissionAppIds));
+                        INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS,
+                        ArrayUtils.convertToIntArray(allPermissionAppIds));
             }
             if (internetPermissionAppIds.size() != 0) {
-                mNetd.trafficSetNetPermForUids(PERMISSION_INTERNET,
-                        convertToIntArray(internetPermissionAppIds));
+                mNetd.trafficSetNetPermForUids(INetd.PERMISSION_INTERNET,
+                        ArrayUtils.convertToIntArray(internetPermissionAppIds));
             }
             if (updateStatsPermissionAppIds.size() != 0) {
-                mNetd.trafficSetNetPermForUids(PERMISSION_UPDATE_DEVICE_STATS,
-                        convertToIntArray(updateStatsPermissionAppIds));
+                mNetd.trafficSetNetPermForUids(INetd.PERMISSION_UPDATE_DEVICE_STATS,
+                        ArrayUtils.convertToIntArray(updateStatsPermissionAppIds));
             }
             if (noPermissionAppIds.size() != 0) {
-                mNetd.trafficSetNetPermForUids(PERMISSION_NONE,
-                        convertToIntArray(noPermissionAppIds));
+                mNetd.trafficSetNetPermForUids(INetd.PERMISSION_NONE,
+                        ArrayUtils.convertToIntArray(noPermissionAppIds));
             }
             if (uninstalledAppIds.size() != 0) {
-                mNetd.trafficSetNetPermForUids(PERMISSION_UNINSTALLED,
-                        convertToIntArray(uninstalledAppIds));
+                mNetd.trafficSetNetPermForUids(INetd.PERMISSION_UNINSTALLED,
+                        ArrayUtils.convertToIntArray(uninstalledAppIds));
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Pass appId list of special permission failed." + e);
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 5484bfc..7175489 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1463,7 +1463,7 @@
             final long token = Binder.clearCallingIdentity();
             List<UserInfo> users;
             try {
-                users = UserManager.get(mContext).getUsers(true);
+                users = UserManager.get(mContext).getAliveUsers();
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 3f949ba..653323d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -25,7 +25,7 @@
  * A helper class to build {@link HdmiCecMessage} from various cec commands.
  */
 public class HdmiCecMessageBuilder {
-    private static final int OSD_NAME_MAX_LENGTH = 13;
+    private static final int OSD_NAME_MAX_LENGTH = 14;
 
     private HdmiCecMessageBuilder() {}
 
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 9ab410d..3fddd5a 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2591,7 +2591,7 @@
             if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
                 mCurMethod = IInputMethod.Stub.asInterface(service);
                 final String curMethodPackage = mCurIntent.getComponent().getPackageName();
-                final int curMethodUid = mPackageManagerInternal.getPackageUidInternal(
+                final int curMethodUid = mPackageManagerInternal.getPackageUid(
                         curMethodPackage, 0 /* flags */, mSettings.getCurrentUserId());
                 if (curMethodUid < 0) {
                     Slog.e(TAG, "Failed to get UID for package=" + curMethodPackage);
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 71f1833..534533f 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -1133,9 +1133,7 @@
             return;
         }
 
-        String dumpFilter = args.length == 0 ? null : args[0];
-
-        ipw.println("Location Manager State:");
+        ipw.print("Location Manager State:");
         ipw.increaseIndent();
         ipw.println("Elapsed Realtime: " + TimeUtils.formatDuration(SystemClock.elapsedRealtime()));
 
@@ -1166,34 +1164,28 @@
                 ipw.println(
                         "Location Controller Extra Package: " + mExtraLocationControllerPackage
                                 + (mExtraLocationControllerPackageEnabled ? " [enabled]"
-                                : "[disabled]"));
+                                : " [disabled]"));
             }
         }
 
         ipw.println("Location Providers:");
         ipw.increaseIndent();
         for (LocationProviderManager manager : mProviderManagers) {
-            if (dumpFilter == null || manager.getName().equals(dumpFilter)) {
-                manager.dump(fd, ipw, args);
-            }
+            manager.dump(fd, ipw, args);
         }
         ipw.decreaseIndent();
 
-        if (dumpFilter == null || GPS_PROVIDER.equals(dumpFilter)) {
-            if (mGnssManagerService != null) {
-                ipw.println("GNSS Manager:");
-                ipw.increaseIndent();
-                mGnssManagerService.dump(fd, ipw, args);
-                ipw.decreaseIndent();
-            }
-        }
-
-        if (dumpFilter == null || "geofence".equals(dumpFilter)) {
-            ipw.println("Geofence Manager:");
+        if (mGnssManagerService != null) {
+            ipw.println("GNSS Manager:");
             ipw.increaseIndent();
-            mGeofenceManager.dump(fd, ipw, args);
+            mGnssManagerService.dump(fd, ipw, args);
             ipw.decreaseIndent();
         }
+
+        ipw.println("Geofence Manager:");
+        ipw.increaseIndent();
+        mGeofenceManager.dump(fd, ipw, args);
+        ipw.decreaseIndent();
     }
 
     private class LocalService extends LocationManagerInternal {
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
index 05aa315..66245a2 100644
--- a/services/core/java/com/android/server/location/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -84,7 +84,7 @@
 import com.android.server.PendingIntentUtils;
 import com.android.server.location.LocationPermissions.PermissionLevel;
 import com.android.server.location.listeners.ListenerMultiplexer;
-import com.android.server.location.listeners.RemovableListenerRegistration;
+import com.android.server.location.listeners.RemoteListenerRegistration;
 import com.android.server.location.util.AppForegroundHelper;
 import com.android.server.location.util.AppForegroundHelper.AppForegroundListener;
 import com.android.server.location.util.AppOpsHelper;
@@ -154,15 +154,8 @@
 
         @Override
         public void deliverOnLocationChanged(Location location,
-                @Nullable Runnable onCompleteCallback)
-                throws RemoteException {
-            mListener.onLocationChanged(location,
-                    onCompleteCallback == null ? null : new IRemoteCallback.Stub() {
-                        @Override
-                        public void sendResult(Bundle data) {
-                            onCompleteCallback.run();
-                        }
-                    });
+                @Nullable Runnable onCompleteCallback) throws RemoteException {
+            mListener.onLocationChanged(location, SingleUseCallback.wrap(onCompleteCallback));
         }
 
         @Override
@@ -221,7 +214,7 @@
     }
 
     protected abstract class Registration extends
-            RemovableListenerRegistration<LocationRequest, LocationTransport> {
+            RemoteListenerRegistration<LocationRequest, LocationTransport> {
 
         @PermissionLevel protected final int mPermissionLevel;
         private final WorkSource mWorkSource;
@@ -306,11 +299,20 @@
         }
 
         @Override
-        protected final void onInactive() {
+        protected final ListenerOperation<LocationTransport> onInactive() {
             onHighPowerUsageChanged();
             if (!getRequest().getHideFromAppOps()) {
                 mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey());
             }
+            return null;
+        }
+
+        @Override
+        public final <Listener> void onOperationFailure(ListenerOperation<Listener> operation,
+                Exception e) {
+            synchronized (mLock) {
+                super.onOperationFailure(operation, e);
+            }
         }
 
         @Override
@@ -818,6 +820,12 @@
         @GuardedBy("mLock")
         @Override
         protected void onProviderListenerRegister() {
+            try {
+                ((IBinder) getKey()).linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                remove();
+            }
+
             mExpirationRealtimeMs = getRequest().getExpirationRealtimeMs(
                     SystemClock.elapsedRealtime());
 
@@ -829,12 +837,6 @@
                         0, this, FgThread.getHandler(), getWorkSource());
             }
 
-            try {
-                ((IBinder) getKey()).linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                remove();
-            }
-
             // start listening for provider enabled/disabled events
             addEnabledListener(this);
 
@@ -1058,8 +1060,13 @@
             mUserInfoHelper.addListener(mUserChangedListener);
             mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
 
-            // initialize enabled state
-            onUserStarted(UserHandle.USER_ALL);
+            long identity = Binder.clearCallingIdentity();
+            try {
+                // initialize enabled state
+                onUserStarted(UserHandle.USER_ALL);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
@@ -1069,10 +1076,15 @@
             mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
 
             // notify and remove all listeners
-            onUserStopped(UserHandle.USER_ALL);
-            removeRegistrationIf(key -> true);
-            mEnabledListeners.clear();
+            long identity = Binder.clearCallingIdentity();
+            try {
+                onUserStopped(UserHandle.USER_ALL);
+                removeRegistrationIf(key -> true);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
 
+            mEnabledListeners.clear();
             mStarted = false;
         }
     }
@@ -1133,14 +1145,26 @@
     public void setRealProvider(AbstractLocationProvider provider) {
         synchronized (mLock) {
             Preconditions.checkState(mStarted);
-            mProvider.setRealProvider(provider);
+
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mProvider.setRealProvider(provider);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
     public void setMockProvider(@Nullable MockProvider provider) {
         synchronized (mLock) {
             Preconditions.checkState(mStarted);
-            mProvider.setMockProvider(provider);
+
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mProvider.setMockProvider(provider);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
 
             // when removing a mock provider, also clear any mock last locations and reset the
             // location fudger. the mock provider could have been used to infer the current
@@ -1162,7 +1186,12 @@
                 throw new IllegalArgumentException(mName + " provider is not a test provider");
             }
 
-            mProvider.setMockProviderAllowed(enabled);
+            long identity = Binder.clearCallingIdentity();
+            try {
+                mProvider.setMockProviderAllowed(enabled);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
@@ -1172,15 +1201,20 @@
                 throw new IllegalArgumentException(mName + " provider is not a test provider");
             }
 
-            String locationProvider = location.getProvider();
-            if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) {
-                // The location has an explicit provider that is different from the mock
-                // provider name. The caller may be trying to fool us via b/33091107.
-                EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
-                        mName + "!=" + locationProvider);
-            }
+            long identity = Binder.clearCallingIdentity();
+            try {
+                String locationProvider = location.getProvider();
+                if (!TextUtils.isEmpty(locationProvider) && !mName.equals(locationProvider)) {
+                    // The location has an explicit provider that is different from the mock
+                    // provider name. The caller may be trying to fool us via b/33091107.
+                    EventLog.writeEvent(0x534e4554, "33091107", Binder.getCallingUid(),
+                            mName + "!=" + locationProvider);
+                }
 
-            mProvider.setMockProviderLocation(location);
+                mProvider.setMockProviderLocation(location);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
@@ -1271,7 +1305,7 @@
         }
     }
 
-    public void getCurrentLocation(LocationRequest request, CallerIdentity identity,
+    public void getCurrentLocation(LocationRequest request, CallerIdentity callerIdentity,
             int permissionLevel, ICancellationSignal cancellationTransport,
             ILocationCallback callback) {
         Preconditions.checkArgument(mName.equals(request.getProvider()));
@@ -1283,12 +1317,12 @@
         GetCurrentLocationListenerRegistration registration =
                 new GetCurrentLocationListenerRegistration(
                         request,
-                        identity,
+                        callerIdentity,
                         new GetCurrentLocationTransport(callback),
                         permissionLevel);
 
         synchronized (mLock) {
-            Location lastLocation = getLastLocation(request, identity, permissionLevel);
+            Location lastLocation = getLastLocation(request, callerIdentity, permissionLevel);
             if (lastLocation != null) {
                 long locationAgeMs = NANOSECONDS.toMillis(
                         SystemClock.elapsedRealtimeNanos()
@@ -1306,7 +1340,13 @@
             }
 
             // if last location isn't good enough then we add a location request
-            addRegistration(callback.asBinder(), registration);
+            long identity = Binder.clearCallingIdentity();
+            try {
+                addRegistration(callback.asBinder(), registration);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+
             CancellationSignal cancellationSignal = CancellationSignal.fromTransport(
                     cancellationTransport);
             if (cancellationSignal != null) {
@@ -1321,48 +1361,73 @@
     }
 
     public void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
-        mProvider.sendExtraCommand(uid, pid, command, extras);
+        long identity = Binder.clearCallingIdentity();
+        try {
+            mProvider.sendExtraCommand(uid, pid, command, extras);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
-    public void registerLocationRequest(LocationRequest request, CallerIdentity identity,
+    public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity,
             @PermissionLevel int permissionLevel, ILocationListener listener) {
         Preconditions.checkArgument(mName.equals(request.getProvider()));
 
         synchronized (mLock) {
-            addRegistration(
-                    listener.asBinder(),
-                    new LocationListenerRegistration(
-                            request,
-                            identity,
-                            new LocationListenerTransport(listener),
-                            permissionLevel));
+            long identity = Binder.clearCallingIdentity();
+            try {
+                addRegistration(
+                        listener.asBinder(),
+                        new LocationListenerRegistration(
+                                request,
+                                callerIdentity,
+                                new LocationListenerTransport(listener),
+                                permissionLevel));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
-    public void registerLocationRequest(LocationRequest request, CallerIdentity identity,
+    public void registerLocationRequest(LocationRequest request, CallerIdentity callerIdentity,
             @PermissionLevel int permissionLevel, PendingIntent pendingIntent) {
         Preconditions.checkArgument(mName.equals(request.getProvider()));
 
         synchronized (mLock) {
-            addRegistration(
-                    pendingIntent,
-                    new LocationPendingIntentRegistration(
-                            request,
-                            identity,
-                            new LocationPendingIntentTransport(mContext, pendingIntent),
-                            permissionLevel));
+            long identity = Binder.clearCallingIdentity();
+            try {
+                addRegistration(
+                        pendingIntent,
+                        new LocationPendingIntentRegistration(
+                                request,
+                                callerIdentity,
+                                new LocationPendingIntentTransport(mContext, pendingIntent),
+                                permissionLevel));
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
     public void unregisterLocationRequest(ILocationListener listener) {
         synchronized (mLock) {
-            removeRegistration(listener.asBinder());
+            long identity = Binder.clearCallingIdentity();
+            try {
+                removeRegistration(listener.asBinder());
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
     public void unregisterLocationRequest(PendingIntent pendingIntent) {
         synchronized (mLock) {
-            removeRegistration(pendingIntent);
+            long identity = Binder.clearCallingIdentity();
+            try {
+                removeRegistration(pendingIntent);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
@@ -1950,4 +2015,50 @@
             }
         }
     }
+
+    private static class SingleUseCallback extends IRemoteCallback.Stub {
+
+        @Nullable
+        public static IRemoteCallback wrap(@Nullable Runnable callback) {
+            return callback == null ? null : new SingleUseCallback(callback);
+        }
+
+        @GuardedBy("this")
+        @Nullable private Runnable mCallback;
+
+        private SingleUseCallback(Runnable callback) {
+            mCallback = Objects.requireNonNull(callback);
+        }
+
+        @Override
+        public void sendResult(Bundle data) {
+            Runnable callback;
+            synchronized (this) {
+                callback = mCallback;
+                mCallback = null;
+            }
+
+            // prevent this callback from being run more than once - otherwise this could provide an
+            // attack vector for a malicious app to break assumptions on how many times a callback
+            // may be invoked, and thus crash system server.
+            if (callback == null) {
+                return;
+            }
+
+            long identity = Binder.clearCallingIdentity();
+            try {
+                callback.run();
+            } catch (RuntimeException e) {
+                // since this is within a oneway binder transaction there is nowhere
+                // for exceptions to go - move onto another thread to crash system
+                // server so we find out about it
+                FgThread.getExecutor().execute(() -> {
+                    throw new AssertionError(e);
+                });
+                throw e;
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 2d9734e..2d7f028 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -32,6 +32,7 @@
 import android.location.LocationManager;
 import android.location.LocationRequest;
 import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.WorkSource;
@@ -291,17 +292,28 @@
             @Nullable String attributionTag) {
         LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_FINE);
 
-        CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag,
-                AppOpsManager.toReceiverId(pendingIntent));
-        addRegistration(new GeofenceKey(pendingIntent, geofence),
-                new GeofenceRegistration(geofence, identity, pendingIntent));
+        CallerIdentity callerIdentity = CallerIdentity.fromBinder(mContext, packageName,
+                attributionTag, AppOpsManager.toReceiverId(pendingIntent));
+
+        long identity = Binder.clearCallingIdentity();
+        try {
+            addRegistration(new GeofenceKey(pendingIntent, geofence),
+                    new GeofenceRegistration(geofence, callerIdentity, pendingIntent));
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
      * Removes the geofence associated with the PendingIntent.
      */
     public void removeGeofence(PendingIntent pendingIntent) {
-        removeRegistrationIf(key -> key.getPendingIntent().equals(pendingIntent));
+        long identity = Binder.clearCallingIdentity();
+        try {
+            removeRegistrationIf(key -> key.getPendingIntent().equals(pendingIntent));
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 1b599b0..a9fdacc 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -25,6 +25,7 @@
 import android.location.LocationManagerInternal;
 import android.location.LocationManagerInternal.ProviderEnabledListener;
 import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.Process;
@@ -218,16 +219,27 @@
     /**
      * Adds a listener with the given identity and request.
      */
-    protected void addListener(TRequest request, CallerIdentity identity, TListener listener) {
-        addRegistration(listener.asBinder(),
-                new GnssListenerRegistration(request, identity, listener));
+    protected void addListener(TRequest request, CallerIdentity callerIdentity,
+            TListener listener) {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            addRegistration(listener.asBinder(),
+                    new GnssListenerRegistration(request, callerIdentity, listener));
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     /**
      * Removes the given listener.
      */
     public void removeListener(TListener listener) {
-        removeRegistration(listener.asBinder());
+        long identity = Binder.clearCallingIdentity();
+        try {
+            removeRegistration(listener.asBinder());
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 850cf7f..5c30fe8 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -2022,6 +2022,21 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        boolean dumpAll = false;
+
+        int opti = 0;
+        while (opti < args.length) {
+            String opt = args[opti];
+            if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') {
+                break;
+            }
+            opti++;
+            if ("-a".equals(opt)) {
+                dumpAll = true;
+                break;
+            }
+        }
+
         StringBuilder s = new StringBuilder();
         s.append("mStarted=").append(mStarted).append("   (changed ");
         TimeUtils.formatDuration(SystemClock.elapsedRealtime()
@@ -2053,9 +2068,11 @@
             s.append("]\n");
         }
         s.append(mGnssMetrics.dumpGnssMetricsAsText());
-        s.append("native internal state: \n");
-        s.append("  ").append(native_get_internal_state());
-        s.append("\n");
+        if (dumpAll) {
+            s.append("native internal state: \n");
+            s.append("  ").append(native_get_internal_state());
+            s.append("\n");
+        }
         pw.append(s);
     }
 
diff --git a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
index bd8bce8..58aabda 100644
--- a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java
@@ -32,10 +32,10 @@
  * @param <TListener> listener type
  */
 public abstract class BinderListenerRegistration<TRequest, TListener> extends
-        RemovableListenerRegistration<TRequest, TListener> implements Binder.DeathRecipient {
+        RemoteListenerRegistration<TRequest, TListener> implements Binder.DeathRecipient {
 
     /**
-     * Interface to allowed binder retrieval when keys are not themselves IBinder.
+     * Interface to allow binder retrieval when keys are not themselves IBinders.
      */
     public interface BinderKey {
         /**
diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
index f94de9b..8a6b8aa 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
@@ -18,12 +18,9 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.Binder;
 import android.os.Build;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-import android.util.IndentingPrintWriter;
-import android.util.Pair;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.listeners.ListenerExecutor.ListenerOperation;
@@ -31,8 +28,10 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.AbstractMap;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.function.Function;
 import java.util.function.Predicate;
@@ -42,7 +41,7 @@
  * divided into two categories, active registrations and inactive registrations, as defined by
  * {@link #isActive(ListenerRegistration)}. If a registration's active state changes,
  * {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration
- * whose active state may have changed.
+ * whose active state may have changed. Listeners will only be invoked for active registrations.
  *
  * Callbacks invoked for various changes will always be ordered according to this lifecycle list:
  *
@@ -64,14 +63,6 @@
  * {@link #removeRegistration(Object, ListenerRegistration)}, not via any other removal method. This
  * ensures re-entrant removal does not accidentally remove the incorrect registration.
  *
- * All callbacks will be invoked with a cleared binder identity.
- *
- * Listeners owned by other processes will be run on a direct executor (and thus while holding a
- * lock). Listeners owned by the same process this multiplexer is in will be run asynchronously (and
- * thus without holding a lock). The underlying assumption is that listeners owned by other
- * processes will simply be forwarding the call to those other processes and perhaps performing
- * simple bookkeeping, with no potential for deadlock.
- *
  * @param <TKey>           key type
  * @param <TRequest>       request type
  * @param <TListener>      listener type
@@ -149,46 +140,51 @@
     }
 
     /**
-     * Invoked before the first registration occurs. This is a convenient entry point for
-     * registering listeners, etc, which only need to be present while there are any registrations.
+     * Invoked when the multiplexer goes from having no registrations to having some registrations.
+     * This is a convenient entry point for registering listeners, etc, which only need to be
+     * present while there are any registrations. Invoked while holding the multiplexer's internal
+     * lock.
      */
     protected void onRegister() {}
 
     /**
-     * Invoked after the last unregistration occurs. This is a convenient entry point for
-     * unregistering listeners, etc, which only need to be present while there are any
-     * registrations.
+     * Invoked when the multiplexer goes from having some registrations to having no registrations.
+     * This is a convenient entry point for unregistering listeners, etc, which only need to be
+     * present while there are any registrations. Invoked while holding the multiplexer's internal
+     * lock.
      */
     protected void onUnregister() {}
 
     /**
-     * Invoked when a registration is added.
+     * Invoked when a registration is added. Invoked while holding the multiplexer's internal lock.
      */
     protected void onRegistrationAdded(@NonNull TKey key, @NonNull TRegistration registration) {}
 
     /**
-     * Invoked when a registration is removed.
+     * Invoked when a registration is removed. Invoked while holding the multiplexer's internal
+     * lock.
      */
     protected void onRegistrationRemoved(@NonNull TKey key, @NonNull TRegistration registration) {}
 
     /**
-     * Invoked when the manager goes from having no active registrations to having some active
+     * Invoked when the multiplexer goes from having no active registrations to having some active
      * registrations. This is a convenient entry point for registering listeners, etc, which only
-     * need to be present while there are active registrations.
+     * need to be present while there are active registrations. Invoked while holding the
+     * multiplexer's internal lock.
      */
     protected void onActive() {}
 
     /**
-     * Invoked when the manager goes from having some active registrations to having no active
+     * Invoked when the multiplexer goes from having some active registrations to having no active
      * registrations. This is a convenient entry point for unregistering listeners, etc, which only
-     * need to be present while there are active registrations.
+     * need to be present while there are active registrations. Invoked while holding the
+     * multiplexer's internal lock.
      */
     protected void onInactive() {}
 
     /**
-     * Adds a new registration with the given key. Registration may fail if
-     * {@link ListenerRegistration#onRegister(Object)} returns false, in which case the registration
-     * will not be added. This method cannot be called to add a registration re-entrantly.
+     * Adds a new registration with the given key. This method cannot be called to add a
+     * registration re-entrantly.
      */
     protected final void addRegistration(@NonNull TKey key, @NonNull TRegistration registration) {
         Objects.requireNonNull(key);
@@ -204,7 +200,6 @@
             // involve removing a prior registration. note that try-with-resources ordering is
             // meaningful here as well. we want to close the reentrancy guard first, as this may
             // generate additional service updates, then close the update service buffer.
-            long identity = Binder.clearCallingIdentity();
             try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
                  ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
 
@@ -224,16 +219,13 @@
                 registration.onRegister(key);
                 onRegistrationAdded(key, registration);
                 onRegistrationActiveChanged(registration);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
             }
         }
     }
 
     /**
-     * Removes the registration with the given key. If unregistration occurs,
-     * {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This method
-     * cannot be called to remove a registration re-entrantly.
+     * Removes the registration with the given key. This method cannot be called to remove a
+     * registration re-entrantly.
      */
     protected final void removeRegistration(@NonNull Object key) {
         synchronized (mRegistrations) {
@@ -250,9 +242,8 @@
     }
 
     /**
-     * Removes all registrations with keys that satisfy the given predicate. If unregistration
-     * occurs, {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This
-     * method cannot be called to remove a registration re-entrantly.
+     * Removes all registrations with keys that satisfy the given predicate. This method cannot be
+     * called to remove a registration re-entrantly.
      */
     protected final void removeRegistrationIf(@NonNull Predicate<TKey> predicate) {
         synchronized (mRegistrations) {
@@ -281,11 +272,8 @@
 
     /**
      * Removes the given registration with the given key. If the given key has a different
-     * registration at the time this method is called, nothing happens. If unregistration occurs,
-     * {@link #onRegistrationRemoved(Object, ListenerRegistration)} will be called. This method
-     * allows for re-entrancy, and may be called to remove a registration re-entrantly. In this case
-     * the registration will immediately be marked inactive and unregistered, and will be removed
-     * completely at some later time.
+     * registration at the time this method is called, nothing happens. This method allows for
+     * re-entrancy, and may be called to remove a registration re-entrantly.
      */
     protected final void removeRegistration(@NonNull Object key,
             @NonNull ListenerRegistration<?, ?> registration) {
@@ -324,7 +312,6 @@
         // in multiple service updates. note that try-with-resources ordering is meaningful here as
         // well. we want to close the reentrancy guard first, as this may generate additional
         // service updates, then close the update service buffer.
-        long identity = Binder.clearCallingIdentity();
         try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
              ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
 
@@ -337,8 +324,6 @@
                     onUnregister();
                 }
             }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
         }
     }
 
@@ -362,34 +347,42 @@
                 }
             }
 
-            long identity = Binder.clearCallingIdentity();
-            try {
-                if (actives.isEmpty()) {
+            if (actives.isEmpty()) {
+                mCurrentRequest = null;
+                if (mServiceRegistered) {
+                    mServiceRegistered = false;
                     mCurrentRequest = null;
-                    if (mServiceRegistered) {
-                        mServiceRegistered = false;
-                        mCurrentRequest = null;
-                        unregisterWithService();
-                    }
-                    return;
+                    unregisterWithService();
                 }
-
-                TMergedRequest merged = mergeRequests(actives);
-                if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) {
-                    if (mServiceRegistered) {
-                        mServiceRegistered = reregisterWithService(mCurrentRequest, merged);
-                    } else {
-                        mServiceRegistered = registerWithService(merged);
-                    }
-                    if (mServiceRegistered) {
-                        mCurrentRequest = merged;
-                    } else {
-                        mCurrentRequest = null;
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
+                return;
             }
+
+            TMergedRequest merged = mergeRequests(actives);
+            if (!mServiceRegistered || !Objects.equals(merged, mCurrentRequest)) {
+                if (mServiceRegistered) {
+                    mServiceRegistered = reregisterWithService(mCurrentRequest, merged);
+                } else {
+                    mServiceRegistered = registerWithService(merged);
+                }
+                if (mServiceRegistered) {
+                    mCurrentRequest = merged;
+                } else {
+                    mCurrentRequest = null;
+                }
+            }
+        }
+    }
+
+    /**
+     * Clears currently stored service state, and invokes {@link #updateService()} to force a new
+     * call to {@link #registerWithService(Object)} if necessary. This is useful, for instance, if
+     * the backing service has crashed or otherwise lost state, and needs to be re-initialized.
+     */
+    protected final void resetService() {
+        synchronized (mRegistrations) {
+            mServiceRegistered = false;
+            mCurrentRequest = null;
+            updateService();
         }
     }
 
@@ -404,9 +397,9 @@
 
     /**
      * Evaluates the predicate on all registrations. The predicate should return true if the active
-     * state of the registration may have changed as a result. Any {@link #updateService()}
-     * invocations made while this method is executing will be deferred until after the method is
-     * complete so as to avoid redundant work.
+     * state of the registration may have changed as a result. If the active state of any
+     * registration has changed, {@link #updateService()} will automatically be invoked to handle
+     * the resulting changes.
      */
     protected final void updateRegistrations(@NonNull Predicate<TRegistration> predicate) {
         synchronized (mRegistrations) {
@@ -415,7 +408,6 @@
             // callbacks. note that try-with-resources ordering is meaningful here as well. we want
             // to close the reentrancy guard first, as this may generate additional service updates,
             // then close the update service buffer.
-            long identity = Binder.clearCallingIdentity();
             try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
                  ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
 
@@ -426,8 +418,6 @@
                         onRegistrationActiveChanged(registration);
                     }
                 }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
             }
         }
     }
@@ -450,7 +440,10 @@
                     execute(registration, operation);
                 }
             } else {
-                registration.onInactive();
+                ListenerOperation<TListener> operation = registration.onInactive();
+                if (operation != null) {
+                    execute(registration, operation);
+                }
                 if (--mActiveRegistrationsCount == 0) {
                     onInactive();
                 }
@@ -468,7 +461,6 @@
     protected final void deliverToListeners(
             @NonNull Function<TRegistration, ListenerOperation<TListener>> function) {
         synchronized (mRegistrations) {
-            long identity = Binder.clearCallingIdentity();
             try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
                 final int size = mRegistrations.size();
                 for (int i = 0; i < size; i++) {
@@ -480,8 +472,6 @@
                         }
                     }
                 }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
             }
         }
     }
@@ -495,7 +485,6 @@
      */
     protected final void deliverToListeners(@NonNull ListenerOperation<TListener> operation) {
         synchronized (mRegistrations) {
-            long identity = Binder.clearCallingIdentity();
             try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
                 final int size = mRegistrations.size();
                 for (int i = 0; i < size; i++) {
@@ -504,8 +493,6 @@
                         execute(registration, operation);
                     }
                 }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
             }
         }
     }
@@ -522,27 +509,26 @@
     /**
      * Dumps debug information.
      */
-    public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         synchronized (mRegistrations) {
-            ipw.print("service: ");
-            dumpServiceState(ipw);
-            ipw.println();
+            pw.print("service: ");
+            dumpServiceState(pw);
+            pw.println();
 
             if (!mRegistrations.isEmpty()) {
-                ipw.println("listeners:");
+                pw.println("listeners:");
 
-                ipw.increaseIndent();
                 final int size = mRegistrations.size();
                 for (int i = 0; i < size; i++) {
                     TRegistration registration = mRegistrations.valueAt(i);
-                    ipw.print(registration);
+                    pw.print("  ");
+                    pw.print(registration);
                     if (!registration.isActive()) {
-                        ipw.println(" (inactive)");
+                        pw.println(" (inactive)");
                     } else {
-                        ipw.println();
+                        pw.println();
                     }
                 }
-                ipw.decreaseIndent();
             }
         }
     }
@@ -577,7 +563,7 @@
         @GuardedBy("mRegistrations")
         private int mGuardCount;
         @GuardedBy("mRegistrations")
-        private @Nullable ArraySet<Pair<Object, ListenerRegistration<?, ?>>> mScheduledRemovals;
+        private @Nullable ArraySet<Entry<Object, ListenerRegistration<?, ?>>> mScheduledRemovals;
 
         ReentrancyGuard() {
             mGuardCount = 0;
@@ -602,7 +588,7 @@
             if (mScheduledRemovals == null) {
                 mScheduledRemovals = new ArraySet<>(mRegistrations.size());
             }
-            mScheduledRemovals.add(new Pair<>(key, registration));
+            mScheduledRemovals.add(new AbstractMap.SimpleImmutableEntry<>(key, registration));
         }
 
         ReentrancyGuard acquire() {
@@ -612,7 +598,7 @@
 
         @Override
         public void close() {
-            ArraySet<Pair<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null;
+            ArraySet<Entry<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null;
 
             Preconditions.checkState(mGuardCount > 0);
             if (--mGuardCount == 0) {
@@ -620,14 +606,15 @@
                 mScheduledRemovals = null;
             }
 
-            if (scheduledRemovals != null) {
-                try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) {
-                    final int size = scheduledRemovals.size();
-                    for (int i = 0; i < size; i++) {
-                        Pair<Object, ListenerRegistration<?, ?>> pair = scheduledRemovals.valueAt(
-                                i);
-                        removeRegistration(pair.first, pair.second);
-                    }
+            if (scheduledRemovals == null) {
+                return;
+            }
+
+            try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) {
+                final int size = scheduledRemovals.size();
+                for (int i = 0; i < size; i++) {
+                    Entry<Object, ListenerRegistration<?, ?>> entry = scheduledRemovals.valueAt(i);
+                    removeRegistration(entry.getKey(), entry.getValue());
                 }
             }
         }
diff --git a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
index ac56c51..deb9660 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerRegistration.java
@@ -17,55 +17,34 @@
 package com.android.server.location.listeners;
 
 
-import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.location.util.identity.CallerIdentity;
-import android.os.Process;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.listeners.ListenerExecutor;
-import com.android.server.FgThread;
 
 import java.util.Objects;
 import java.util.concurrent.Executor;
 
 /**
  * A listener registration object which holds data associated with the listener, such as an optional
- * request, and the identity of the listener owner.
+ * request, and an executor responsible for listener invocations.
  *
  * @param <TRequest>  request type
  * @param <TListener> listener type
  */
 public class ListenerRegistration<TRequest, TListener> implements ListenerExecutor {
 
-    @VisibleForTesting
-    public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor();
-
     private final Executor mExecutor;
     private final @Nullable TRequest mRequest;
-    private final CallerIdentity mIdentity;
 
     private boolean mActive;
 
     private volatile @Nullable TListener mListener;
 
-    protected ListenerRegistration(@Nullable TRequest request, CallerIdentity identity,
+    protected ListenerRegistration(Executor executor, @Nullable TRequest request,
             TListener listener) {
-        // if a client is in the same process as us, binder calls will execute synchronously and
-        // we shouldn't run callbacks directly since they might be run under lock and deadlock
-        if (identity.getPid() == Process.myPid()) {
-            // there's a slight loophole here for pending intents - pending intent callbacks can
-            // always be run on the direct executor since they're always asynchronous, but honestly
-            // you shouldn't be using pending intent callbacks within the same process anyways
-            mExecutor = IN_PROCESS_EXECUTOR;
-        } else {
-            mExecutor = DIRECT_EXECUTOR;
-        }
-
+        mExecutor = Objects.requireNonNull(executor);
         mRequest = request;
-        mIdentity = Objects.requireNonNull(identity);
         mActive = false;
         mListener = Objects.requireNonNull(listener);
     }
@@ -82,34 +61,34 @@
     }
 
     /**
-     * Returns the listener identity.
-     */
-    public final CallerIdentity getIdentity() {
-        return mIdentity;
-    }
-
-    /**
-     * May be overridden by subclasses. Invoked when registration occurs.
+     * May be overridden by subclasses. Invoked when registration occurs. Invoked while holding the
+     * owning multiplexer's internal lock.
      */
     protected void onRegister(Object key) {}
 
     /**
-     * May be overridden by subclasses. Invoked when unregistration occurs.
+     * May be overridden by subclasses. Invoked when unregistration occurs. Invoked while holding
+     * the owning multiplexer's internal lock.
      */
     protected void onUnregister() {}
 
     /**
      * May be overridden by subclasses. Invoked when this registration becomes active. If this
-     * returns a non-null operation, that operation will be invoked for the listener.
+     * returns a non-null operation, that operation will be invoked for the listener. Invoked
+     * while holding the owning multiplexer's internal lock.
      */
     protected @Nullable ListenerOperation<TListener> onActive() {
         return null;
     }
 
     /**
-     * May be overridden by subclasses. Invoked when registration becomes inactive.
+     * May be overridden by subclasses. Invoked when registration becomes inactive. If this returns
+     * a non-null operation, that operation will be invoked for the listener. Invoked while holding
+     * the owning multiplexer's internal lock.
      */
-    protected void onInactive() {}
+    protected @Nullable ListenerOperation<TListener> onInactive() {
+        return null;
+    }
 
     public final boolean isActive() {
         return mActive;
@@ -136,8 +115,7 @@
     /**
      * May be overridden by subclasses, however should rarely be needed. Invoked when the listener
      * associated with this registration is unregistered, which may occur before the registration
-     * itself is unregistered. This immediately prevents the listener from being further invoked
-     * even if the various bookkeeping associated with unregistration has not occurred yet.
+     * itself is unregistered. This immediately prevents the listener from being further invoked.
      */
     protected void onListenerUnregister() {};
 
diff --git a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
index b5d2ef6..7b6154e 100644
--- a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java
@@ -30,7 +30,7 @@
  * @param <TListener> listener type
  */
 public abstract class PendingIntentListenerRegistration<TRequest, TListener> extends
-        RemovableListenerRegistration<TRequest, TListener> implements PendingIntent.CancelListener {
+        RemoteListenerRegistration<TRequest, TListener> implements PendingIntent.CancelListener {
 
     /**
      * Interface to allowed pending intent retrieval when keys are not themselves PendingIntents.
diff --git a/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java
new file mode 100644
index 0000000..e4b0b19
--- /dev/null
+++ b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java
@@ -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.
+ */
+
+package com.android.server.location.listeners;
+
+
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+
+import android.annotation.Nullable;
+import android.location.util.identity.CallerIdentity;
+import android.os.Process;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.FgThread;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * A listener registration representing a remote (possibly from a different process) listener.
+ * Listeners from a different process will be run on a direct executor, since the x-process listener
+ * invocation should already be asynchronous. Listeners from the same process will be run on a
+ * normal executor, since in-process listener invocation may be synchronous.
+ *
+ * @param <TRequest>  request type
+ * @param <TListener> listener type
+ */
+public abstract class RemoteListenerRegistration<TRequest, TListener> extends
+        RemovableListenerRegistration<TRequest, TListener> {
+
+    @VisibleForTesting
+    public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor();
+
+    private static Executor chooseExecutor(CallerIdentity identity) {
+        // if a client is in the same process as us, binder calls will execute synchronously and
+        // we shouldn't run callbacks directly since they might be run under lock and deadlock
+        if (identity.getPid() == Process.myPid()) {
+            // there's a slight loophole here for pending intents - pending intent callbacks can
+            // always be run on the direct executor since they're always asynchronous, but honestly
+            // you shouldn't be using pending intent callbacks within the same process anyways
+            return IN_PROCESS_EXECUTOR;
+        } else {
+            return DIRECT_EXECUTOR;
+        }
+    }
+
+    private final CallerIdentity mIdentity;
+
+    protected RemoteListenerRegistration(String tag, @Nullable TRequest request,
+            CallerIdentity identity, TListener listener) {
+        super(tag, chooseExecutor(identity), request, listener);
+        mIdentity = Objects.requireNonNull(identity);
+    }
+
+    /**
+     * Returns the listener identity.
+     */
+    public final CallerIdentity getIdentity() {
+        return mIdentity;
+    }
+}
+
diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
index 0698cca..2383bec 100644
--- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
@@ -17,10 +17,10 @@
 package com.android.server.location.listeners;
 
 import android.annotation.Nullable;
-import android.location.util.identity.CallerIdentity;
 import android.util.Log;
 
 import java.util.Objects;
+import java.util.concurrent.Executor;
 
 /**
  * A listener registration that stores its own key, and thus can remove itself. By default it will
@@ -36,9 +36,9 @@
 
     private volatile @Nullable Object mKey;
 
-    protected RemovableListenerRegistration(String tag, @Nullable TRequest request,
-            CallerIdentity callerIdentity, TListener listener) {
-        super(request, callerIdentity, listener);
+    protected RemovableListenerRegistration(String tag, Executor executor,
+            @Nullable TRequest request, TListener listener) {
+        super(executor, request, listener);
         mTag = Objects.requireNonNull(tag);
     }
 
@@ -70,7 +70,7 @@
 
     @Override
     public <Listener> void onOperationFailure(ListenerOperation<Listener> operation, Exception e) {
-        Log.w(mTag, "registration " + getIdentity() + " removed due to unexpected exception", e);
+        Log.w(mTag, "registration " + this + " removed due to unexpected exception", e);
         remove();
     }
 
diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
index e3074db..ddd56c8 100644
--- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
+++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java
@@ -123,6 +123,10 @@
                 final VerifyCredentialResponse response = spManager.verifyChallengeInternal(
                         getGatekeeperService(), userAuthInfo.gatekeeperPassword, challenge,
                         userAuthInfo.userId);
+                if (response == null) {
+                    Slog.wtf(TAG, "VerifyChallenge failed, null response");
+                    continue;
+                }
                 if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
                     Slog.wtf(TAG, "VerifyChallenge failed, response: "
                             + response.getResponseCode());
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 0044d89..26c3132 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -33,7 +33,7 @@
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
 import static com.android.internal.widget.LockPatternUtils.USER_FRP;
-import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_RETURN_GK_PW;
+import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_REQUEST_GK_PW_HANDLE;
 import static com.android.internal.widget.LockPatternUtils.frpCredentialEnabled;
 import static com.android.internal.widget.LockPatternUtils.userOwnsFrpCredential;
 
@@ -104,6 +104,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.EventLog;
+import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
 
@@ -123,7 +124,6 @@
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
 import com.android.server.locksettings.LockSettingsStorage.CredentialHash;
 import com.android.server.locksettings.LockSettingsStorage.PersistentData;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
@@ -155,6 +155,7 @@
 import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
+import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -186,6 +187,14 @@
     private static final String SYNTHETIC_PASSWORD_UPDATE_TIME_KEY = "sp-handle-ts";
     private static final String USER_SERIAL_NUMBER_KEY = "serial-number";
 
+    // Duration that LockSettingsService will store the gatekeeper password for. This allows
+    // multiple biometric enrollments without prompting the user to enter their password via
+    // ConfirmLockPassword/ConfirmLockPattern multiple times. This needs to be at least the duration
+    // from the start of the first biometric sensor's enrollment to the start of the last biometric
+    // sensor's enrollment. If biometric enrollment requests a password handle that has expired, the
+    // user's credential must be presented again, e.g. via ConfirmLockPattern/ConfirmLockPassword.
+    private static final int GK_PW_HANDLE_STORE_DURATION_MS = 10 * 60 * 1000; // 10 minutes
+
     // Order of holding lock: mSeparateChallengeLock -> mSpManager -> this
     // Do not call into ActivityManager while holding mSpManager lock.
     private final Object mSeparateChallengeLock = new Object();
@@ -202,6 +211,8 @@
     private final LockSettingsStrongAuth mStrongAuth;
     private final SynchronizedStrongAuthTracker mStrongAuthTracker;
     private final BiometricDeferredQueue mBiometricDeferredQueue;
+    private final LongSparseArray<byte[]> mGatekeeperPasswords;
+    private final Random mRandom;
 
     private final NotificationManager mNotificationManager;
     private final UserManager mUserManager;
@@ -559,6 +570,8 @@
         mStorageManager = injector.getStorageManager();
         mStrongAuthTracker = injector.getStrongAuthTracker();
         mStrongAuthTracker.register(mStrongAuth);
+        mGatekeeperPasswords = new LongSparseArray<>();
+        mRandom = new SecureRandom();
 
         mSpManager = injector.getSyntheticPasswordManager(mStorage);
         mManagedProfilePasswordCache = injector.getManagedProfilePasswordCache();
@@ -1017,7 +1030,7 @@
         mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsWrite");
     }
 
-    private final void checkPasswordReadPermission(int userId) {
+    private final void checkPasswordReadPermission() {
         mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsRead");
     }
 
@@ -1077,7 +1090,9 @@
     public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
             LockscreenCredential managedUserPassword) {
         checkWritePermission(userId);
-        if (!mHasSecureLockScreen) {
+        if (!mHasSecureLockScreen
+                && managedUserPassword != null
+                && managedUserPassword.getType() != CREDENTIAL_TYPE_NONE) {
             throw new UnsupportedOperationException(
                     "This operation requires secure lock screen feature.");
         }
@@ -1547,7 +1562,8 @@
     public boolean setLockCredential(LockscreenCredential credential,
             LockscreenCredential savedCredential, int userId) {
 
-        if (!mHasSecureLockScreen) {
+        if (!mHasSecureLockScreen
+                && credential != null && credential.getType() != CREDENTIAL_TYPE_NONE) {
             throw new UnsupportedOperationException(
                     "This operation requires secure lock screen feature");
         }
@@ -1923,7 +1939,7 @@
     @Override
     public VerifyCredentialResponse checkCredential(LockscreenCredential credential, int userId,
             ICheckCredentialProgressCallback progressCallback) {
-        checkPasswordReadPermission(userId);
+        checkPasswordReadPermission();
         try {
             return doVerifyCredential(credential, userId, progressCallback, 0 /* flags */);
         } finally {
@@ -1935,7 +1951,7 @@
     @Nullable
     public VerifyCredentialResponse verifyCredential(LockscreenCredential credential,
             int userId, int flags) {
-        checkPasswordReadPermission(userId);
+        checkPasswordReadPermission();
         try {
             return doVerifyCredential(credential, userId, null /* progressCallback */, flags);
         } finally {
@@ -1944,18 +1960,36 @@
     }
 
     @Override
-    public VerifyCredentialResponse verifyGatekeeperPassword(byte[] gatekeeperPassword,
+    public VerifyCredentialResponse verifyGatekeeperPasswordHandle(long gatekeeperPasswordHandle,
             long challenge, int userId) {
-        checkPasswordReadPermission(userId);
+        checkPasswordReadPermission();
 
-        VerifyCredentialResponse response;
+        final VerifyCredentialResponse response;
+        final byte[] gatekeeperPassword;
+
+        synchronized (mGatekeeperPasswords) {
+            gatekeeperPassword = mGatekeeperPasswords.get(gatekeeperPasswordHandle);
+        }
+
         synchronized (mSpManager) {
-            response = mSpManager.verifyChallengeInternal(getGateKeeperService(),
-                    gatekeeperPassword, challenge, userId);
+            if (gatekeeperPassword == null) {
+                response = VerifyCredentialResponse.ERROR;
+            } else {
+                response = mSpManager.verifyChallengeInternal(getGateKeeperService(),
+                        gatekeeperPassword, challenge, userId);
+            }
         }
         return response;
     }
 
+    @Override
+    public void removeGatekeeperPasswordHandle(long gatekeeperPasswordHandle) {
+        checkPasswordReadPermission();
+        synchronized (mGatekeeperPasswords) {
+            mGatekeeperPasswords.remove(gatekeeperPasswordHandle);
+        }
+    }
+
     /*
      * Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero
      * format.
@@ -2012,7 +2046,7 @@
     @Override
     public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential,
             int userId, @LockPatternUtils.VerifyFlag int flags) {
-        checkPasswordReadPermission(userId);
+        checkPasswordReadPermission();
         if (!isManagedProfileWithUnifiedLock(userId)) {
             throw new IllegalArgumentException("User id must be managed profile with unified lock");
         }
@@ -2179,7 +2213,7 @@
         }
         mFirstCallToVold = false;
 
-        checkPasswordReadPermission(userId);
+        checkPasswordReadPermission();
 
         // There's no guarantee that this will safely connect, but if it fails
         // we will simply show the lock screen when we shouldn't, so relatively
@@ -2269,13 +2303,13 @@
 
     @Override
     public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
-        checkPasswordReadPermission(UserHandle.USER_ALL);
+        checkPasswordReadPermission();
         mStrongAuth.registerStrongAuthTracker(tracker);
     }
 
     @Override
     public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) {
-        checkPasswordReadPermission(UserHandle.USER_ALL);
+        checkPasswordReadPermission();
         mStrongAuth.unregisterStrongAuthTracker(tracker);
     }
 
@@ -2305,7 +2339,7 @@
 
     @Override
     public int getStrongAuthForUser(int userId) {
-        checkPasswordReadPermission(userId);
+        checkPasswordReadPermission();
         return mStrongAuthTracker.getStrongAuthForUser(userId);
     }
 
@@ -2648,7 +2682,7 @@
 
         final AuthenticationResult authResult;
         VerifyCredentialResponse response;
-        final boolean returnGkPw = (flags & VERIFY_FLAG_RETURN_GK_PW) != 0;
+        final boolean requestGkPw = (flags & VERIFY_FLAG_REQUEST_GK_PW_HANDLE) != 0;
 
         synchronized (mSpManager) {
             if (!isSyntheticPasswordBasedCredentialLocked(userId)) {
@@ -2690,14 +2724,43 @@
             }
         }
 
-        if (response.isMatched() && returnGkPw) {
-            return new VerifyCredentialResponse.Builder()
-                    .setGatekeeperPassword(authResult.authToken.deriveGkPassword()).build();
+        if (response.isMatched() && requestGkPw) {
+            final long handle = storeGatekeeperPasswordTemporarily(
+                    authResult.authToken.deriveGkPassword());
+            return new VerifyCredentialResponse.Builder().setGatekeeperPasswordHandle(handle)
+                    .build();
         } else {
             return response;
         }
     }
 
+    /**
+     * Stores the gatekeeper password temporarily.
+     * @param gatekeeperPassword unlocked upon successful Synthetic Password
+     * @return non-zero handle to the gatekeeper password, which can be used for a set amount of
+     *         time.
+     */
+    private long storeGatekeeperPasswordTemporarily(byte[] gatekeeperPassword) {
+        long handle = 0L;
+
+        synchronized (mGatekeeperPasswords) {
+            while (handle == 0L || mGatekeeperPasswords.get(handle) != null) {
+                handle = mRandom.nextLong();
+            }
+            mGatekeeperPasswords.put(handle, gatekeeperPassword);
+        }
+
+        final long finalHandle = handle;
+        mHandler.postDelayed(() -> {
+            synchronized (mGatekeeperPasswords) {
+                Slog.d(TAG, "Removing handle: " + finalHandle);
+                mGatekeeperPasswords.remove(finalHandle);
+            }
+        }, GK_PW_HANDLE_STORE_DURATION_MS);
+
+        return handle;
+    }
+
     private void onCredentialVerified(AuthenticationToken authToken, PasswordMetrics metrics,
             int userId) {
 
@@ -2935,7 +2998,7 @@
      */
     @Override
     public byte[] getHashFactor(LockscreenCredential currentCredential, int userId) {
-        checkPasswordReadPermission(userId);
+        checkPasswordReadPermission();
         try {
             if (isManagedProfileWithUnifiedLock(userId)) {
                 try {
@@ -3013,7 +3076,7 @@
 
     @Override
     public boolean hasPendingEscrowToken(int userId) {
-        checkPasswordReadPermission(userId);
+        checkPasswordReadPermission();
         synchronized (mSpManager) {
             return !mSpManager.getPendingTokensForUser(userId).isEmpty();
         }
@@ -3199,6 +3262,8 @@
         mRebootEscrowManager.dump(pw);
         pw.println();
         pw.decreaseIndent();
+
+        pw.println("PasswordHandleCount: " + mGatekeeperPasswords.size());
     }
 
     /**
@@ -3361,7 +3426,8 @@
         @Override
         public boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle,
                 byte[] token, int userId) {
-            if (!mHasSecureLockScreen) {
+        if (!mHasSecureLockScreen
+                && credential != null && credential.getType() != CREDENTIAL_TYPE_NONE) {
                 throw new UnsupportedOperationException(
                         "This operation requires secure lock screen feature.");
             }
diff --git a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 0575ac6..d202a2a 100644
--- a/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/services/core/java/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -100,11 +100,16 @@
             if (match != null) continue;
 
             // Create listener for every newly added sub. Also store subscriberId into it to
-            // prevent binder call to telephony when querying RAT.
+            // prevent binder call to telephony when querying RAT. If the subscriberId is empty
+            // for any reason, such as SIM PIN locked, skip registration.
+            // SubscriberId will be unavailable again if 1. modem crashed 2. reboot
+            // 3. re-insert SIM. If that happens, the listeners will be eventually synchronized
+            // with active sub list once all subscriberIds are ready.
             final String subscriberId = mTeleManager.getSubscriberId(subId);
             if (TextUtils.isEmpty(subscriberId)) {
-                Log.wtf(NetworkStatsService.TAG,
-                        "Empty subscriberId for newly added sub: " + subId);
+                Log.d(NetworkStatsService.TAG, "Empty subscriberId for newly added sub "
+                        + subId + ", skip listener registration");
+                continue;
             }
             final RatTypeListener listener =
                     new RatTypeListener(mExecutor, this, subId, subscriberId);
@@ -113,6 +118,7 @@
             // Register listener to the telephony manager that associated with specific sub.
             mTeleManager.createForSubscriptionId(subId)
                     .listen(listener, PhoneStateListener.LISTEN_SERVICE_STATE);
+            Log.d(NetworkStatsService.TAG, "RAT type listener registered for sub " + subId);
         }
 
         for (final RatTypeListener listener : new ArrayList<>(mRatListeners)) {
@@ -165,6 +171,7 @@
     private void handleRemoveRatTypeListener(@NonNull RatTypeListener listener) {
         mTeleManager.createForSubscriptionId(listener.mSubId)
                 .listen(listener, PhoneStateListener.LISTEN_NONE);
+        Log.d(NetworkStatsService.TAG, "RAT type listener unregistered for sub " + listener.mSubId);
         mRatListeners.remove(listener);
 
         // Removal of subscriptions doesn't generate RAT changed event, fire it for every
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 9f9235d..0465855 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6103,7 +6103,8 @@
     protected boolean isBlocked(NotificationRecord r, NotificationUsageStats usageStats) {
         if (isBlocked(r)) {
             if (DBG) {
-                Slog.e(TAG, "Suppressing notification from package by user request.");
+                Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName()
+                        + " by user request.");
             }
             usageStats.registerBlocked(r);
             return true;
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
new file mode 100644
index 0000000..d8745ab
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -0,0 +1,310 @@
+/*
+ * 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.pm;
+
+import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA256;
+import static android.content.pm.PackageManager.PARTIAL_MERKLE_ROOT_1M_SHA512;
+import static android.content.pm.PackageManager.WHOLE_MD5;
+import static android.content.pm.PackageManager.WHOLE_MERKLE_ROOT_4K_SHA256;
+import static android.content.pm.PackageManager.WHOLE_SHA1;
+import static android.content.pm.PackageManager.WHOLE_SHA256;
+import static android.content.pm.PackageManager.WHOLE_SHA512;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
+import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
+
+import android.annotation.Nullable;
+import android.content.pm.FileChecksum;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.util.ArrayMap;
+import android.util.Slog;
+import android.util.apk.ApkSignatureSchemeV2Verifier;
+import android.util.apk.ApkSignatureSchemeV3Verifier;
+import android.util.apk.ApkSignatureSchemeV4Verifier;
+import android.util.apk.ApkSignatureVerifier;
+import android.util.apk.ApkSigningBlockUtils;
+import android.util.apk.ByteBufferFactory;
+import android.util.apk.SignatureInfo;
+import android.util.apk.SignatureNotFoundException;
+import android.util.apk.VerityBuilder;
+
+import com.android.server.security.VerityUtils;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.Certificate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides checksums for APK.
+ */
+public class ApkChecksums {
+    static final String TAG = "ApkChecksums";
+
+    // MessageDigest algorithms.
+    static final String ALGO_MD5 = "MD5";
+    static final String ALGO_SHA1 = "SHA1";
+    static final String ALGO_SHA256 = "SHA256";
+    static final String ALGO_SHA512 = "SHA512";
+
+    /**
+     * Fetch or calculate checksums for the specific file.
+     *
+     * @param split             split name, null for base
+     * @param file              to fetch checksums for
+     * @param optional          mask to fetch readily available checksums
+     * @param required          mask to forcefully calculate if not available
+     * @param trustedInstallers array of certificate to trust, two specific cases:
+     *                          null - trust anybody,
+     *                          [] - trust nobody.
+     */
+    public static List<FileChecksum> getFileChecksums(String split, File file,
+            @PackageManager.FileChecksumKind int optional,
+            @PackageManager.FileChecksumKind int required,
+            @Nullable Certificate[] trustedInstallers) {
+        final String filePath = file.getAbsolutePath();
+        Map<Integer, FileChecksum> checksums = new ArrayMap<>();
+        final int kinds = (optional | required);
+        // System enforced: FSI or v2/v3/v4 signatures.
+        if ((kinds & WHOLE_MERKLE_ROOT_4K_SHA256) != 0) {
+            // Hashes in fs-verity and IncFS are always verified.
+            FileChecksum checksum = extractHashFromFS(split, filePath);
+            if (checksum != null) {
+                checksums.put(checksum.getKind(), checksum);
+            }
+        }
+        if ((kinds & (PARTIAL_MERKLE_ROOT_1M_SHA256 | PARTIAL_MERKLE_ROOT_1M_SHA512)) != 0) {
+            Map<Integer, FileChecksum> v2v3checksums = extractHashFromV2V3Signature(
+                    split, filePath, kinds);
+            if (v2v3checksums != null) {
+                checksums.putAll(v2v3checksums);
+            }
+        }
+
+        // TODO(b/160605420): Installer provided.
+        // TODO(b/160605420): Wait for Incremental to be fully loaded.
+
+        // Manually calculating required checksums if not readily available.
+        if ((required & WHOLE_MERKLE_ROOT_4K_SHA256) != 0 && !checksums.containsKey(
+                WHOLE_MERKLE_ROOT_4K_SHA256)) {
+            try {
+                byte[] generatedRootHash = VerityBuilder.generateFsVerityRootHash(
+                        filePath, /*salt=*/null,
+                        new ByteBufferFactory() {
+                            @Override
+                            public ByteBuffer create(int capacity) {
+                                return ByteBuffer.allocate(capacity);
+                            }
+                        });
+                checksums.put(WHOLE_MERKLE_ROOT_4K_SHA256,
+                        new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, generatedRootHash));
+            } catch (IOException | NoSuchAlgorithmException | DigestException e) {
+                Slog.e(TAG, "Error calculating WHOLE_MERKLE_ROOT_4K_SHA256", e);
+            }
+        }
+
+        calculateChecksumIfRequested(checksums, split, file, required, WHOLE_MD5);
+        calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA1);
+        calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA256);
+        calculateChecksumIfRequested(checksums, split, file, required, WHOLE_SHA512);
+
+        calculatePartialChecksumsIfRequested(checksums, split, file, required);
+
+        return new ArrayList<>(checksums.values());
+    }
+
+    private static FileChecksum extractHashFromFS(String split, String filePath) {
+        // verity first
+        {
+            byte[] hash = VerityUtils.getFsverityRootHash(filePath);
+            if (hash != null) {
+                return new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash);
+            }
+        }
+        // v4 next
+        try {
+            ApkSignatureSchemeV4Verifier.VerifiedSigner signer =
+                    ApkSignatureSchemeV4Verifier.extractCertificates(filePath);
+            byte[] hash = signer.contentDigests.getOrDefault(CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
+                    null);
+            if (hash != null) {
+                return new FileChecksum(split, WHOLE_MERKLE_ROOT_4K_SHA256, hash);
+            }
+        } catch (SignatureNotFoundException e) {
+            // Nothing
+        } catch (SecurityException e) {
+            Slog.e(TAG, "V4 signature error", e);
+        }
+        return null;
+    }
+
+    private static Map<Integer, FileChecksum> extractHashFromV2V3Signature(
+            String split, String filePath, int kinds) {
+        Map<Integer, byte[]> contentDigests = null;
+        try {
+            contentDigests = ApkSignatureVerifier.verifySignaturesInternal(filePath,
+                    PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
+                    false).contentDigests;
+        } catch (PackageParser.PackageParserException e) {
+            Slog.e(TAG, "Signature verification error", e);
+        }
+
+        if (contentDigests == null) {
+            return null;
+        }
+
+        Map<Integer, FileChecksum> checksums = new ArrayMap<>();
+        if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0) {
+            byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA256, null);
+            if (hash != null) {
+                checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA256,
+                        new FileChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA256, hash));
+            }
+        }
+        if ((kinds & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0) {
+            byte[] hash = contentDigests.getOrDefault(CONTENT_DIGEST_CHUNKED_SHA512, null);
+            if (hash != null) {
+                checksums.put(PARTIAL_MERKLE_ROOT_1M_SHA512,
+                        new FileChecksum(split, PARTIAL_MERKLE_ROOT_1M_SHA512, hash));
+            }
+        }
+        return checksums;
+    }
+
+    private static String getMessageDigestAlgoForChecksumKind(int kind)
+            throws NoSuchAlgorithmException {
+        switch (kind) {
+            case WHOLE_MD5:
+                return ALGO_MD5;
+            case WHOLE_SHA1:
+                return ALGO_SHA1;
+            case WHOLE_SHA256:
+                return ALGO_SHA256;
+            case WHOLE_SHA512:
+                return ALGO_SHA512;
+            default:
+                throw new NoSuchAlgorithmException("Invalid checksum kind: " + kind);
+        }
+    }
+
+    private static void calculateChecksumIfRequested(Map<Integer, FileChecksum> checksums,
+            String split, File file, int required, int kind) {
+        if ((required & kind) != 0 && !checksums.containsKey(kind)) {
+            final byte[] checksum = getFileChecksum(file, kind);
+            if (checksum != null) {
+                checksums.put(kind, new FileChecksum(split, kind, checksum));
+            }
+        }
+    }
+
+    private static byte[] getFileChecksum(File file, int kind) {
+        try (FileInputStream fis = new FileInputStream(file);
+             BufferedInputStream bis = new BufferedInputStream(fis)) {
+            byte[] dataBytes = new byte[512 * 1024];
+            int nread = 0;
+
+            final String algo = getMessageDigestAlgoForChecksumKind(kind);
+            MessageDigest md = MessageDigest.getInstance(algo);
+            while ((nread = bis.read(dataBytes)) != -1) {
+                md.update(dataBytes, 0, nread);
+            }
+
+            return md.digest();
+        } catch (IOException e) {
+            Slog.e(TAG, "Error reading " + file.getAbsolutePath() + " to compute hash.", e);
+            return null;
+        } catch (NoSuchAlgorithmException e) {
+            Slog.e(TAG, "Device does not support MessageDigest algorithm", e);
+            return null;
+        }
+    }
+
+    private static int[] getContentDigestAlgos(boolean needSignatureSha256,
+            boolean needSignatureSha512) {
+        if (needSignatureSha256 && needSignatureSha512) {
+            // Signature block present, but no digests???
+            return new int[]{CONTENT_DIGEST_CHUNKED_SHA256, CONTENT_DIGEST_CHUNKED_SHA512};
+        } else if (needSignatureSha256) {
+            return new int[]{CONTENT_DIGEST_CHUNKED_SHA256};
+        } else {
+            return new int[]{CONTENT_DIGEST_CHUNKED_SHA512};
+        }
+    }
+
+    private static int getChecksumKindForContentDigestAlgo(int contentDigestAlgo) {
+        switch (contentDigestAlgo) {
+            case CONTENT_DIGEST_CHUNKED_SHA256:
+                return PARTIAL_MERKLE_ROOT_1M_SHA256;
+            case CONTENT_DIGEST_CHUNKED_SHA512:
+                return PARTIAL_MERKLE_ROOT_1M_SHA512;
+            default:
+                return -1;
+        }
+    }
+
+    private static void calculatePartialChecksumsIfRequested(Map<Integer, FileChecksum> checksums,
+            String split, File file, int required) {
+        boolean needSignatureSha256 =
+                (required & PARTIAL_MERKLE_ROOT_1M_SHA256) != 0 && !checksums.containsKey(
+                        PARTIAL_MERKLE_ROOT_1M_SHA256);
+        boolean needSignatureSha512 =
+                (required & PARTIAL_MERKLE_ROOT_1M_SHA512) != 0 && !checksums.containsKey(
+                        PARTIAL_MERKLE_ROOT_1M_SHA512);
+        if (!needSignatureSha256 && !needSignatureSha512) {
+            return;
+        }
+
+        try (RandomAccessFile raf = new RandomAccessFile(file, "r")) {
+            SignatureInfo signatureInfo = null;
+            try {
+                signatureInfo = ApkSignatureSchemeV3Verifier.findSignature(raf);
+            } catch (SignatureNotFoundException e) {
+                try {
+                    signatureInfo = ApkSignatureSchemeV2Verifier.findSignature(raf);
+                } catch (SignatureNotFoundException ee) {
+                }
+            }
+            if (signatureInfo == null) {
+                Slog.e(TAG, "V2/V3 signatures not found in " + file.getAbsolutePath());
+                return;
+            }
+
+            final int[] digestAlgos = getContentDigestAlgos(needSignatureSha256,
+                    needSignatureSha512);
+            byte[][] digests = ApkSigningBlockUtils.computeContentDigestsPer1MbChunk(digestAlgos,
+                    raf.getFD(), signatureInfo);
+            for (int i = 0, size = digestAlgos.length; i < size; ++i) {
+                int checksumKind = getChecksumKindForContentDigestAlgo(digestAlgos[i]);
+                if (checksumKind != -1) {
+                    checksums.put(checksumKind, new FileChecksum(split, checksumKind, digests[i]));
+                }
+            }
+        } catch (IOException | DigestException e) {
+            Slog.e(TAG, "Error computing hash.", e);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 617f687..a234f5a 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -605,7 +605,7 @@
     private boolean isPlatformSignedAppWithAutomaticProfilesPermission(
             String packageName, int[] profileIds) {
         for (int userId : profileIds) {
-            final int uid = mInjector.getPackageManagerInternal().getPackageUidInternal(
+            final int uid = mInjector.getPackageManagerInternal().getPackageUid(
                     packageName, /* flags= */ 0, userId);
             if (uid == -1) {
                 continue;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 28c5e96..ed62362 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -134,6 +134,7 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.dex.DexManager;
@@ -1325,6 +1326,7 @@
                 final int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
                         PackageInstaller.STATUS_FAILURE);
                 final int sessionIndex = mChildSessionsRemaining.indexOfKey(sessionId);
+                final String message = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
                 if (PackageInstaller.STATUS_SUCCESS == status) {
                     mChildSessionsRemaining.removeAt(sessionIndex);
                     if (mChildSessionsRemaining.size() == 0) {
@@ -1341,10 +1343,9 @@
                     intent.putExtra(PackageInstaller.EXTRA_SESSION_ID,
                             PackageInstallerSession.this.sessionId);
                     mChildSessionsRemaining.clear(); // we're done. Don't send any more.
-                    try {
-                        mStatusReceiver.sendIntent(mContext, 0, intent, null, null);
-                    } catch (IntentSender.SendIntentException ignore) {
-                    }
+                    destroyInternal();
+                    dispatchSessionFinished(INSTALL_FAILED_INTERNAL_ERROR,
+                            "Child session " + sessionId + " failed: " + message, null);
                 }
             });
         }
@@ -1554,12 +1555,28 @@
     }
 
     private void onSessionValidationFailure(int error, String detailMessage) {
-        // Session is sealed but could not be verified, we need to destroy it.
+        // Session is sealed but could not be validated, we need to destroy it.
         destroyInternal();
         // Dispatch message to remove session from PackageInstallerService.
         dispatchSessionFinished(error, detailMessage, null);
     }
 
+    private void onSessionVerificationFailure(int error, String detailMessage) {
+        Slog.e(TAG, "Failed to verify session " + sessionId + " [" + detailMessage + "]");
+        // Session is sealed and committed but could not be verified, we need to destroy it.
+        destroyInternal();
+        if (isStaged()) {
+            setStagedSessionFailed(
+                    SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, detailMessage);
+            // TODO(b/136257624): Remove this once all verification logic has been transferred out
+            //  of StagingManager.
+            mStagingManager.notifyVerificationComplete(sessionId);
+        } else {
+            // Dispatch message to remove session from PackageInstallerService.
+            dispatchSessionFinished(error, detailMessage, null);
+        }
+    }
+
     private void onStorageUnhealthy() {
         final String packageName = getPackageName();
         if (TextUtils.isEmpty(packageName)) {
@@ -1680,7 +1697,8 @@
         }
         if (params.isStaged) {
             mStagingManager.commitSession(this);
-            destroyInternal();
+            // TODO(b/136257624): CTS test fails if we don't send session finished broadcast, even
+            //  though ideally, we just need to send session committed broadcast.
             dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null);
             return;
         }
@@ -1691,14 +1709,30 @@
                     "APEX packages can only be installed using staged sessions.", null);
             return;
         }
+        verify();
+    }
 
+    /**
+     * Resumes verification process for non-final committed staged session.
+     *
+     * Useful if a device gets rebooted before verification is complete and we need to restart the
+     * verification.
+     */
+    void verifyStagedSession() {
+        assertCallerIsOwnerOrRootOrSystemLocked();
+        Preconditions.checkArgument(isCommitted());
+        Preconditions.checkArgument(isStaged());
+        Preconditions.checkArgument(!mStagedSessionApplied && !mStagedSessionFailed);
+
+        verify();
+    }
+
+    private void verify() {
         try {
             verifyNonStaged();
         } catch (PackageManagerException e) {
             final String completeMsg = ExceptionUtils.getCompleteMessage(e);
-            Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
-            destroyInternal();
-            dispatchSessionFinished(e.error, completeMsg, null);
+            onSessionVerificationFailure(e.error, completeMsg);
         }
     }
 
@@ -1846,7 +1880,9 @@
     @GuardedBy("mLock")
     private PackageManagerService.VerificationParams makeVerificationParamsLocked()
             throws PackageManagerException {
-        if (!params.isMultiPackage) {
+        // TODO(b/136257624): Some logic in this if block probably belongs in
+        //  makeInstallParams().
+        if (!params.isMultiPackage && !isApexInstallation()) {
             Objects.requireNonNull(mPackageName);
             Objects.requireNonNull(mSigningDetails);
             Objects.requireNonNull(mResolvedBaseFile);
@@ -1923,8 +1959,7 @@
                     if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
                         onVerificationComplete();
                     } else {
-                        destroyInternal();
-                        dispatchSessionFinished(returnCode, msg, extras);
+                        onSessionVerificationFailure(returnCode, msg);
                     }
                 }
             };
@@ -1946,9 +1981,13 @@
     }
 
     private void onVerificationComplete() {
-        if ((params.installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
-            destroyInternal();
-            dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Dry run", new Bundle());
+        // Staged sessions will be installed later during boot
+        if (isStaged()) {
+            // TODO(b/136257624): Remove this once all verification logic has been transferred out
+            //  of StagingManager.
+            mStagingManager.notifyPreRebootVerification_Apk_Complete(sessionId);
+            // TODO(b/136257624): We also need to destroy internals for verified staged session,
+            //  otherwise file descriptors are never closed for verified staged session until reboot
             return;
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 5fba8b9..344f9cf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -40,6 +40,7 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
 import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
 import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
@@ -168,6 +169,7 @@
 import android.content.pm.DataLoaderType;
 import android.content.pm.FallbackCategoryProvider;
 import android.content.pm.FeatureInfo;
+import android.content.pm.FileChecksum;
 import android.content.pm.IDexModuleRegisterCallback;
 import android.content.pm.IPackageChangeObserver;
 import android.content.pm.IPackageDataObserver;
@@ -254,6 +256,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Parcel;
+import android.os.ParcelableException;
 import android.os.PatternMatcher;
 import android.os.PersistableBundle;
 import android.os.Process;
@@ -394,6 +397,7 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -404,7 +408,10 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.PublicKey;
 import java.security.SecureRandom;
+import java.security.cert.Certificate;
 import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -2400,9 +2407,12 @@
         final int callingUserId = UserHandle.getUserId(callingUid);
 
         for (String packageName : packages) {
-            PackageSetting setting = mSettings.mPackages.get(packageName);
-            if (setting != null
-                    && !shouldFilterApplicationLocked(setting, callingUid, callingUserId)) {
+            final boolean filterApp;
+            synchronized (mLock) {
+                final PackageSetting ps = mSettings.getPackageLPr(packageName);
+                filterApp = shouldFilterApplicationLocked(ps, callingUid, callingUserId);
+            }
+            if (!filterApp) {
                 notifyInstallObserver(packageName);
             }
         }
@@ -2443,6 +2453,83 @@
         mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
     }
 
+    @Override
+    public void getChecksums(@NonNull String packageName, boolean includeSplits,
+            @PackageManager.FileChecksumKind int optional,
+            @PackageManager.FileChecksumKind int required, @Nullable List trustedInstallers,
+            @NonNull IntentSender statusReceiver, int userId) {
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(statusReceiver);
+
+        final ApplicationInfo applicationInfo = getApplicationInfoInternal(packageName, 0,
+                Binder.getCallingUid(), userId);
+        if (applicationInfo == null) {
+            throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
+        }
+
+        List<Pair<String, File>> filesToChecksum = new ArrayList<>();
+
+        // Adding base split.
+        filesToChecksum.add(Pair.create(null, new File(applicationInfo.sourceDir)));
+
+        // Adding other splits.
+        if (includeSplits && applicationInfo.splitNames != null) {
+            for (int i = 0, size = applicationInfo.splitNames.length; i < size; ++i) {
+                filesToChecksum.add(Pair.create(applicationInfo.splitNames[i],
+                        new File(applicationInfo.splitSourceDirs[i])));
+            }
+        }
+
+        for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
+            final File file = filesToChecksum.get(i).second;
+            if (!file.exists()) {
+                throw new IllegalStateException("File not found: " + file.getPath());
+            }
+        }
+
+        final Certificate[] trustedCerts = (trustedInstallers != null) ? decodeCertificates(
+                trustedInstallers) : null;
+        final Context context = mContext;
+
+        mInjector.getBackgroundExecutor().execute(() -> {
+            final Intent intent = new Intent();
+            List<FileChecksum> result = new ArrayList<>();
+            for (int i = 0, size = filesToChecksum.size(); i < size; ++i) {
+                final String split = filesToChecksum.get(i).first;
+                final File file = filesToChecksum.get(i).second;
+                try {
+                    result.addAll(ApkChecksums.getFileChecksums(split, file, optional, required,
+                            trustedCerts));
+                } catch (Throwable e) {
+                    Slog.e(TAG, "Checksum calculation error", e);
+                }
+            }
+            intent.putExtra(EXTRA_CHECKSUMS,
+                    result.toArray(new FileChecksum[result.size()]));
+
+            try {
+                statusReceiver.sendIntent(context, 1, intent, null, null);
+            } catch (SendIntentException e) {
+                Slog.w(TAG, e);
+            }
+        });
+    }
+
+    private static @NonNull Certificate[] decodeCertificates(@NonNull List certs) {
+        try {
+            final CertificateFactory cf = CertificateFactory.getInstance("X.509");
+            final Certificate[] result = new Certificate[certs.size()];
+            for (int i = 0, size = certs.size(); i < size; ++i) {
+                final InputStream is = new ByteArrayInputStream((byte[]) certs.get(i));
+                final X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
+                result[i] = cert;
+            }
+            return result;
+        } catch (CertificateException e) {
+            throw ExceptionUtils.propagate(e);
+        }
+    }
+
     /**
      * Gets the type of the external storage a package is installed on.
      * @param packageVolume The storage volume of the package.
@@ -5806,10 +5893,6 @@
     private void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) {
         for (int i = userList.length - 1; i >= 0; --i) {
             final int userId = userList[i];
-            // don't add instant app to the list of updates
-            if (pkgSetting.getInstantApp(userId)) {
-                continue;
-            }
             SparseArray<String> changedPackages = mChangedPackages.get(userId);
             if (changedPackages == null) {
                 changedPackages = new SparseArray<>();
@@ -5854,6 +5937,11 @@
             for (int i = sequenceNumber; i < mChangedPackagesSequenceNumber; i++) {
                 final String packageName = changedPackages.get(i);
                 if (packageName != null) {
+                    // Filter out the changes if the calling package should not be able to see it.
+                    final PackageSetting ps = mSettings.mPackages.get(packageName);
+                    if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
+                        continue;
+                    }
                     packageNames.add(packageName);
                 }
             }
@@ -8963,10 +9051,10 @@
         if (providerInfo == null) {
             return null;
         }
-        if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) {
-            return null;
-        }
         synchronized (mLock) {
+            if (!mSettings.isEnabledAndMatchLPr(providerInfo, flags, userId)) {
+                return null;
+            }
             final PackageSetting ps = mSettings.mPackages.get(providerInfo.packageName);
             final ComponentName component =
                     new ComponentName(providerInfo.packageName, providerInfo.name);
@@ -9053,9 +9141,11 @@
             String targetPackage, int flags) {
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
-        final PackageSetting ps = mSettings.mPackages.get(targetPackage);
-        if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
-            return ParceledListSlice.emptyList();
+        synchronized (mLock) {
+            final PackageSetting ps = mSettings.getPackageLPr(targetPackage);
+            if (shouldFilterApplicationLocked(ps, callingUid, callingUserId)) {
+                return ParceledListSlice.emptyList();
+            }
         }
         return new ParceledListSlice<>(queryInstrumentationInternal(targetPackage, flags,
                 callingUserId));
@@ -11586,7 +11676,7 @@
             configurePackageComponents(parsedPackage);
         }
 
-        final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride, pkgSetting);
+        final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride);
         final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();
 
         if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
@@ -11680,8 +11770,9 @@
 
         if (DEBUG_ABI_SELECTION) {
             Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()
-                    + " to root=" + parsedPackage.getNativeLibraryRootDir() + ", isa="
-                    + parsedPackage.isNativeLibraryRootRequiresIsa());
+                    + " to root=" + parsedPackage.getNativeLibraryRootDir()
+                    + ", to dir=" + parsedPackage.getNativeLibraryDir()
+                    + ", isa=" + parsedPackage.isNativeLibraryRootRequiresIsa());
         }
 
         // Push the derived path down into PackageSettings so we know what to
@@ -11689,9 +11780,10 @@
         pkgSetting.legacyNativeLibraryPathString = parsedPackage.getNativeLibraryRootDir();
 
         if (DEBUG_ABI_SELECTION) {
-            Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are" +
-                    " primary=" + AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage) +
-                    " secondary=" + AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));
+            Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"
+                    + " primary=" + pkgSetting.primaryCpuAbiString
+                    + " secondary=" + pkgSetting.primaryCpuAbiString
+                    + " abiOverride=" + pkgSetting.cpuAbiOverrideString);
         }
 
         if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
@@ -13790,7 +13882,7 @@
         final boolean isCallerOwner = isCallerDeviceOrProfileOwner(userId);
         final long callingId = Binder.clearCallingIdentity();
         try {
-            final String activeLauncherPackageName = getActiveLauncherPackageName(userId);
+            final String activeLauncherPackageName = mPermissionManager.getDefaultHome(userId);
             final String dialerPackageName = mPermissionManager.getDefaultDialer(userId);
             for (int i = 0; i < packageNames.length; i++) {
                 canSuspend[i] = false;
@@ -13866,18 +13958,6 @@
         return canSuspend;
     }
 
-    private String getActiveLauncherPackageName(int userId) {
-        Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.addCategory(Intent.CATEGORY_HOME);
-        ResolveInfo resolveInfo = resolveIntent(
-                intent,
-                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                PackageManager.MATCH_DEFAULT_ONLY,
-                userId);
-
-        return resolveInfo == null ? null : resolveInfo.activityInfo.packageName;
-    }
-
     @Override
     public void verifyPendingInstall(int id, int verificationCode) throws RemoteException {
         mContext.enforceCallingOrSelfPermission(
@@ -14569,7 +14649,7 @@
         final PackageSetting ps;
         int appId = -1;
         long ceDataInode = -1;
-        synchronized (mSettings) {
+        synchronized (mLock) {
             ps = mSettings.getPackageLPr(packageName);
             if (ps != null) {
                 appId = ps.appId;
@@ -15198,6 +15278,13 @@
         }
 
         public void handleStartCopy() {
+            if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
+                // Apex packages get verified in StagingManager currently.
+                // TODO(b/136257624): Move apex verification logic out of StagingManager
+                mRet = INSTALL_SUCCEEDED;
+                return;
+            }
+
             PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                     origin.resolvedPath, installFlags, packageAbiOverride);
 
@@ -17610,12 +17697,13 @@
                 }
                 boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null
                         && pkgSetting.getPkgState().isUpdatedSystemApp();
+                final String abiOverride = deriveAbiOverride(args.abiOverride);
                 AndroidPackage oldPackage = mPackages.get(pkgName);
                 boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
                 final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
                         derivedAbi = mInjector.getAbiHelper().derivePackageAbi(parsedPackage,
                         isUpdatedSystemAppFromExistingSetting || isUpdatedSystemAppInferred,
-                        args.abiOverride);
+                        abiOverride);
                 derivedAbi.first.applyTo(parsedPackage);
                 derivedAbi.second.applyTo(parsedPackage);
             } catch (PackageManagerException pme) {
@@ -19442,6 +19530,9 @@
         }
 
         if (outInfo != null) {
+            if ((flags & PackageManager.DELETE_KEEP_DATA) == 0) {
+                outInfo.dataRemoved = true;
+            }
             outInfo.removedPackage = ps.name;
             outInfo.installerPackageName = ps.installSource.installerPackageName;
             outInfo.isStaticSharedLib = pkg != null && pkg.getStaticSharedLibName() != null;
@@ -19477,9 +19568,11 @@
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, false /* checkShell */, "clear application data");
 
-        final PackageSetting ps = mSettings.getPackageLPr(packageName);
-        final boolean filterApp =
-                (ps != null && shouldFilterApplicationLocked(ps, callingUid, userId));
+        final boolean filterApp;
+        synchronized (mLock) {
+            final PackageSetting ps = mSettings.getPackageLPr(packageName);
+            filterApp = shouldFilterApplicationLocked(ps, callingUid, userId);
+        }
         if (!filterApp && mProtectedPackages.isPackageDataProtected(userId, packageName)) {
             throw new SecurityException("Cannot clear data for a protected package: "
                     + packageName);
@@ -19759,11 +19852,13 @@
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.SET_PREFERRED_APPLICATIONS)
                 != PackageManager.PERMISSION_GRANTED) {
-            if (getUidTargetSdkVersionLockedLPr(callingUid)
-                    < Build.VERSION_CODES.FROYO) {
-                Slog.w(TAG, "Ignoring addPreferredActivity() from uid "
-                        + callingUid);
-                return;
+            synchronized (mLock) {
+                if (getUidTargetSdkVersionLockedLPr(callingUid)
+                        < Build.VERSION_CODES.FROYO) {
+                    Slog.w(TAG, "Ignoring addPreferredActivity() from uid "
+                            + callingUid);
+                    return;
+                }
             }
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
@@ -19943,8 +20038,9 @@
     /** This method takes a specific user id as well as UserHandle.USER_ALL. */
     private void clearPackagePreferredActivities(String packageName, int userId) {
         final SparseBooleanArray changedUsers = new SparseBooleanArray();
-
-        clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId);
+        synchronized (mLock) {
+            clearPackagePreferredActivitiesLPw(packageName, changedUsers, userId);
+        }
         if (changedUsers.size() > 0) {
             updateDefaultHomeNotLocked(changedUsers);
             postPreferredActivityChangedBroadcast(userId);
@@ -20066,7 +20162,9 @@
         // writer
         try {
             final SparseBooleanArray changedUsers = new SparseBooleanArray();
-            clearPackagePreferredActivitiesLPw(null, changedUsers, userId);
+            synchronized (mLock) {
+                clearPackagePreferredActivitiesLPw(null, changedUsers, userId);
+            }
             if (changedUsers.size() > 0) {
                 postPreferredActivityChangedBroadcast(userId);
             }
@@ -20534,6 +20632,9 @@
         if (cn != null) {
             return cn;
         }
+        // TODO: This should not happen since there should always be a default package set for
+        //  ROLE_HOME in RoleManager. Continue with a warning log for now.
+        Slog.w(TAG, "Default package for ROLE_HOME is not set in RoleManager");
 
         // Find the launcher with the highest priority and return that component if there are no
         // other home activity with the same priority.
@@ -20582,6 +20683,7 @@
         if (packageName == null) {
             return null;
         }
+
         int resolveInfosSize = resolveInfos.size();
         for (int i = 0; i < resolveInfosSize; i++) {
             ResolveInfo resolveInfo = resolveInfos.get(i);
@@ -20641,6 +20743,11 @@
             // PermissionController manages default home directly.
             return false;
         }
+
+        if (packageName == null) {
+            // Keep the default home package in RoleManager.
+            return false;
+        }
         mPermissionManager.setDefaultHome(packageName, userId, (successful) -> {
             if (successful) {
                 postPreferredActivityChangedBroadcast(userId);
@@ -21076,15 +21183,19 @@
         // Limit who can change which apps
         if (!UserHandle.isSameApp(callingUid, pkgSetting.appId)) {
             // Don't allow apps that don't have permission to modify other apps
-            if (!allowedByPermission
-                    || shouldFilterApplicationLocked(pkgSetting, callingUid, userId)) {
+            final boolean filterApp;
+            synchronized (mLock) {
+                filterApp = (!allowedByPermission
+                        || shouldFilterApplicationLocked(pkgSetting, callingUid, userId));
+            }
+            if (filterApp) {
                 throw new SecurityException(
                         "Attempt to change component state; "
-                        + "pid=" + Binder.getCallingPid()
-                        + ", uid=" + callingUid
-                        + (className == null
+                                + "pid=" + Binder.getCallingPid()
+                                + ", uid=" + callingUid
+                                + (className == null
                                 ? ", package=" + packageName
-                                : ", component=" + packageName + "/" + className));
+                                        : ", component=" + packageName + "/" + className));
             }
             // Don't allow changing protected packages.
             if (mProtectedPackages.isPackageStateProtected(userId, packageName)) {
@@ -24600,12 +24711,6 @@
         @Override
         public int getPackageUid(String packageName, int flags, int userId) {
             return PackageManagerService.this
-                    .getPackageUid(packageName, flags, userId);
-        }
-
-        @Override
-        public int getPackageUidInternal(String packageName, int flags, int userId) {
-            return PackageManagerService.this
                     .getPackageUidInternal(packageName, flags, userId, Process.SYSTEM_UID);
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 491b4fc..5553cd0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -421,18 +421,13 @@
 
     /**
      * Derive the value of the {@code cpuAbiOverride} based on the provided
-     * value and an optional stored value from the package settings.
+     * value.
      */
-    public static String deriveAbiOverride(String abiOverride, PackageSetting settings) {
-        String cpuAbiOverride = null;
+    public static String deriveAbiOverride(String abiOverride) {
         if (NativeLibraryHelper.CLEAR_ABI_OVERRIDE.equals(abiOverride)) {
-            cpuAbiOverride = null;
-        } else if (abiOverride != null) {
-            cpuAbiOverride = abiOverride;
-        } else if (settings != null) {
-            cpuAbiOverride = settings.cpuAbiOverrideString;
+            return null;
         }
-        return cpuAbiOverride;
+        return abiOverride;
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 32ec052..4d2e22a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -103,6 +103,7 @@
 import com.android.internal.content.PackageHelper;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
@@ -138,7 +139,7 @@
     private static final String STDIN_PATH = "-";
     /** Path where ART profiles snapshots are dumped for the shell user */
     private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/";
-    private static final int DEFAULT_WAIT_MS = 60 * 1000;
+    private static final int DEFAULT_STAGED_READY_TIMEOUT_MS = 60 * 1000;
     private static final String TAG = "PackageManagerShellCommand";
 
     final IPackageManager mInterface;
@@ -463,9 +464,20 @@
         return 1;
     }
 
-    private int runRollbackApp() {
+    private int runRollbackApp() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
 
+        String opt;
+        long stagedReadyTimeoutMs = DEFAULT_STAGED_READY_TIMEOUT_MS;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "--staged-ready-timeout":
+                    stagedReadyTimeoutMs = Long.parseLong(getNextArgRequired());
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown option: " + opt);
+            }
+        }
         final String packageName = getNextArgRequired();
         if (packageName == null) {
             pw.println("Error: package name not specified");
@@ -495,14 +507,21 @@
         final Intent result = receiver.getResult();
         final int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
                 RollbackManager.STATUS_FAILURE);
-        if (status == RollbackManager.STATUS_SUCCESS) {
-            pw.println("Success");
-            return 0;
-        } else {
+
+        if (status != RollbackManager.STATUS_SUCCESS) {
             pw.println("Failure ["
                     + result.getStringExtra(RollbackManager.EXTRA_STATUS_MESSAGE) + "]");
             return 1;
         }
+
+        if (rollback.isStaged() && stagedReadyTimeoutMs > 0) {
+            final int committedSessionId = rollback.getCommittedSessionId();
+            return doWaitForStagedSessionReady(committedSessionId, stagedReadyTimeoutMs, pw);
+        }
+
+        pw.println("Success");
+        return 0;
+
     }
 
     private void setParamsSize(InstallParams params, List<String> inPaths) {
@@ -1307,11 +1326,12 @@
             }
             abandonSession = false;
 
-            if (!params.sessionParams.isStaged || !params.mWaitForStagedSessionReady) {
-                pw.println("Success");
-                return 0;
+            if (params.sessionParams.isStaged && params.stagedReadyTimeoutMs > 0) {
+                return doWaitForStagedSessionReady(sessionId, params.stagedReadyTimeoutMs, pw);
             }
-            return doWaitForStagedSessionRead(sessionId, params.timeoutMs, pw);
+
+            pw.println("Success");
+            return 0;
         } finally {
             if (abandonSession) {
                 try {
@@ -1322,11 +1342,9 @@
         }
     }
 
-    private int doWaitForStagedSessionRead(int sessionId, long timeoutMs, PrintWriter pw)
+    private int doWaitForStagedSessionReady(int sessionId, long timeoutMs, PrintWriter pw)
               throws RemoteException {
-        if (timeoutMs <= 0) {
-            timeoutMs = DEFAULT_WAIT_MS;
-        }
+        Preconditions.checkArgument(timeoutMs > 0);
         PackageInstaller.SessionInfo si = mInterface.getPackageInstaller()
                 .getSessionInfo(sessionId);
         if (si == null) {
@@ -1376,25 +1394,14 @@
     private int runInstallCommit() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
         String opt;
-        boolean waitForStagedSessionReady = true;
-        long timeoutMs = -1;
+        long stagedReadyTimeoutMs = DEFAULT_STAGED_READY_TIMEOUT_MS;
         while ((opt = getNextOption()) != null) {
             switch (opt) {
-                case "--wait-for-staged-ready":
-                    waitForStagedSessionReady = true;
-                    // If there is only one remaining argument, then it represents the sessionId, we
-                    // shouldn't try to parse it as timeoutMs.
-                    if (getRemainingArgsCount() > 1) {
-                        try {
-                            timeoutMs = Long.parseLong(peekNextArg());
-                            getNextArg();
-                        } catch (NumberFormatException ignore) {
-                        }
-                    }
+                case "--staged-ready-timeout":
+                    stagedReadyTimeoutMs = Long.parseLong(getNextArgRequired());
                     break;
-                case "--no-wait":
-                    waitForStagedSessionReady = false;
-                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown option: " + opt);
             }
         }
         final int sessionId = Integer.parseInt(getNextArg());
@@ -1403,11 +1410,11 @@
         }
         final PackageInstaller.SessionInfo si = mInterface.getPackageInstaller()
                 .getSessionInfo(sessionId);
-        if (si == null || !si.isStaged() || !waitForStagedSessionReady) {
-            pw.println("Success");
-            return 0;
+        if (si != null && si.isStaged() && stagedReadyTimeoutMs > 0) {
+            return doWaitForStagedSessionReady(sessionId, stagedReadyTimeoutMs, pw);
         }
-        return doWaitForStagedSessionRead(sessionId, timeoutMs, pw);
+        pw.println("Success");
+        return 0;
     }
 
     private int runInstallCreate() throws RemoteException {
@@ -2738,8 +2745,7 @@
         SessionParams sessionParams;
         String installerPackageName;
         int userId = UserHandle.USER_ALL;
-        boolean mWaitForStagedSessionReady = true;
-        long timeoutMs = DEFAULT_WAIT_MS;
+        long stagedReadyTimeoutMs = DEFAULT_STAGED_READY_TIMEOUT_MS;
     }
 
     private InstallParams makeInstallParams() {
@@ -2868,16 +2874,8 @@
                     }
                     sessionParams.installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;
                     break;
-                case "--wait-for-staged-ready":
-                    params.mWaitForStagedSessionReady = true;
-                    try {
-                        params.timeoutMs = Long.parseLong(peekNextArg());
-                        getNextArg();
-                    } catch (NumberFormatException ignore) {
-                    }
-                    break;
-                case "--no-wait":
-                    params.mWaitForStagedSessionReady = false;
+                case "--staged-ready-timeout":
+                    params.stagedReadyTimeoutMs = Long.parseLong(getNextArgRequired());
                     break;
                 case "--skip-verification":
                     sessionParams.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
@@ -3199,12 +3197,24 @@
         return 0;
     }
 
+    private long getFileStatSize(File file) {
+        final ParcelFileDescriptor pfd = openFileForSystem(file.getPath(), "r");
+        if (pfd == null) {
+            throw new IllegalArgumentException("Error: Can't open file: " + file.getPath());
+        }
+        try {
+            return pfd.getStatSize();
+        } finally {
+            IoUtils.closeQuietly(pfd);
+        }
+    }
+
     private void processArgForLocalFile(String arg, PackageInstaller.Session session) {
         final String inPath = arg;
 
         final File file = new File(inPath);
         final String name = file.getName();
-        final long size = file.length();
+        final long size = getFileStatSize(file);
         final Metadata metadata = Metadata.forLocalFile(inPath);
 
         byte[] v4signatureBytes = null;
@@ -3601,7 +3611,7 @@
         pw.println("       [--preload] [--instant] [--full] [--dont-kill]");
         pw.println("       [--enable-rollback]");
         pw.println("       [--force-uuid internal|UUID] [--pkg PACKAGE] [-S BYTES]");
-        pw.println("       [--apex] [--wait-for-staged-ready TIMEOUT]");
+        pw.println("       [--apex] [--staged-ready-timeout TIMEOUT]");
         pw.println("       [PATH [SPLIT...]|-]");
         pw.println("    Install an application.  Must provide the apk data to install, either as");
         pw.println("    file path(s) or '-' to read from stdin.  Options are:");
@@ -3629,9 +3639,11 @@
         pw.println("          3=device setup, 4=user request");
         pw.println("      --force-uuid: force install on to disk volume with given UUID");
         pw.println("      --apex: install an .apex file, not an .apk");
-        pw.println("      --wait-for-staged-ready: when performing staged install, wait TIMEOUT");
-        pw.println("          ms for pre-reboot verification to complete. If TIMEOUT is not");
-        pw.println("          specified it will wait for " + DEFAULT_WAIT_MS + " milliseconds.");
+        pw.println("      --staged-ready-timeout: By default, staged sessions wait "
+                + DEFAULT_STAGED_READY_TIMEOUT_MS);
+        pw.println("          milliseconds for pre-reboot verification to complete when");
+        pw.println("          performing staged install. This flag is used to alter the waiting");
+        pw.println("          time. You can skip the waiting time by specifying a TIMEOUT of '0'");
         pw.println("");
         pw.println("  install-existing [--user USER_ID|all|current]");
         pw.println("       [--instant] [--full] [--wait] [--restrict-permissions] PACKAGE");
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index d545bd4..659e2a3 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4551,6 +4551,7 @@
                     ? "true" : "false");
             pw.print(prefix); pw.print("  primaryCpuAbi="); pw.println(ps.primaryCpuAbiString);
             pw.print(prefix); pw.print("  secondaryCpuAbi="); pw.println(ps.secondaryCpuAbiString);
+            pw.print(prefix); pw.print("  cpuAbiOverride="); pw.println(ps.cpuAbiOverrideString);
         }
         pw.print(prefix); pw.print("  versionCode="); pw.print(ps.versionCode);
         if (pkg != null) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 700f7be..8412077 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -3301,7 +3301,7 @@
 
                 final long token = Binder.clearCallingIdentity();
                 try {
-                    int packageUid = mPackageManagerInternal.getPackageUidInternal(packageName,
+                    int packageUid = mPackageManagerInternal.getPackageUid(packageName,
                             PackageManager.MATCH_DIRECT_BOOT_AUTO, userId);
                     // Grant read uri permission to the caller on behalf of the shortcut owner. All
                     // granted permissions are revoked when the default launcher changes, or when
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 6d80f08..0c4eaec3 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -88,7 +88,6 @@
 import java.util.Set;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
 
@@ -189,7 +188,6 @@
      * Validates the signature used to sign the container of the new apex package
      *
      * @param newApexPkg The new apex package that is being installed
-     * @throws PackageManagerException
      */
     private void validateApexSignature(PackageInfo newApexPkg)
             throws PackageManagerException {
@@ -725,12 +723,9 @@
         return ret;
     }
 
-    @NonNull
     private PackageInstallerSession createAndWriteApkSession(
-            @NonNull PackageInstallerSession originalSession, boolean preReboot)
-            throws PackageManagerException {
-        final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED
-                : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED;
+            PackageInstallerSession originalSession) throws PackageManagerException {
+        final int errorCode = SessionInfo.STAGED_SESSION_ACTIVATION_FAILED;
         if (originalSession.stageDir == null) {
             Slog.wtf(TAG, "Attempting to install a staged APK session with no staging dir");
             throw new PackageManagerException(errorCode,
@@ -746,12 +741,7 @@
         PackageInstaller.SessionParams params = originalSession.params.copy();
         params.isStaged = false;
         params.installFlags |= PackageManager.INSTALL_STAGED;
-        if (preReboot) {
-            params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
-            params.installFlags |= PackageManager.INSTALL_DRY_RUN;
-        } else {
-            params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
-        }
+        params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
         try {
             int apkSessionId = mPi.createSession(
                     params, originalSession.getInstallerPackageName(),
@@ -783,12 +773,10 @@
      * apks in the given session. Only parent session is returned for multi-package session.
      */
     @Nullable
-    private PackageInstallerSession extractApksInSession(PackageInstallerSession session,
-            boolean preReboot) throws PackageManagerException {
-        final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED
-                : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED;
+    private PackageInstallerSession extractApksInSession(PackageInstallerSession session)
+            throws PackageManagerException {
         if (!session.isMultiPackage() && !isApexSession(session)) {
-            return createAndWriteApkSession(session, preReboot);
+            return createAndWriteApkSession(session);
         } else if (session.isMultiPackage()) {
             // For multi-package staged sessions containing APKs, we identify which child sessions
             // contain an APK, and with those then create a new multi-package group of sessions,
@@ -810,10 +798,6 @@
             }
             final PackageInstaller.SessionParams params = session.params.copy();
             params.isStaged = false;
-            if (preReboot) {
-                params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
-                params.installFlags |= PackageManager.INSTALL_DRY_RUN;
-            }
             final int apkParentSessionId = mPi.createSession(
                     params, session.getInstallerPackageName(), session.getInstallerAttributionTag(),
                     session.userId);
@@ -823,18 +807,18 @@
             } catch (IOException e) {
                 Slog.e(TAG, "Unable to prepare multi-package session for staged session "
                         + session.sessionId);
-                throw new PackageManagerException(errorCode,
+                throw new PackageManagerException(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                         "Unable to prepare multi-package session for staged session");
             }
 
             for (int i = 0, size = childSessions.size(); i < size; i++) {
                 final PackageInstallerSession apkChildSession = createAndWriteApkSession(
-                        childSessions.get(i), preReboot);
+                        childSessions.get(i));
                 try {
                     apkParentSession.addChildSessionId(apkChildSession.sessionId);
                 } catch (IllegalStateException e) {
                     Slog.e(TAG, "Failed to add a child session for installing the APK files", e);
-                    throw new PackageManagerException(errorCode,
+                    throw new PackageManagerException(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED,
                             "Failed to add a child session " + apkChildSession.sessionId);
                 }
             }
@@ -877,11 +861,9 @@
         }
     }
 
-    private void installApksInSession(@NonNull PackageInstallerSession session)
+    private void installApksInSession(PackageInstallerSession session)
             throws PackageManagerException {
-
-        final PackageInstallerSession apksToInstall = extractApksInSession(
-                session, /* preReboot */ false);
+        final PackageInstallerSession apksToInstall = extractApksInSession(session);
         if (apksToInstall == null) {
             return;
         }
@@ -1005,7 +987,7 @@
                             // will be deleted.
                         }
                         root.setStagedSessionFailed(
-                                SessionInfo.STAGED_SESSION_OTHER_ERROR,
+                                SessionInfo.STAGED_SESSION_CONFLICT,
                                 "Session was blocking rollback session: " + session.sessionId);
                         Slog.i(TAG, "Session " + root.sessionId + " is marked failed due to "
                                 + "blocking rollback session: " + session.sessionId);
@@ -1047,7 +1029,7 @@
     /**
      * <p>Abort committed staged session
      *
-     * <p>This method must be called while holding {@link PackageInstallerSession.mLock}.
+     * <p>This method must be called while holding {@link PackageInstallerSession#mLock}.
      *
      * <p>The method returns {@code false} to indicate it is not safe to clean up the session from
      * system yet. When it is safe, the method returns {@code true}.
@@ -1218,7 +1200,7 @@
         }
     }
 
-    void markStagedSessionsAsSuccessful() {
+    private void markStagedSessionsAsSuccessful() {
         synchronized (mSuccessfulStagedSessionIds) {
             for (int i = 0; i < mSuccessfulStagedSessionIds.size(); i++) {
                 mApexManager.markStagedSessionSuccessful(mSuccessfulStagedSessionIds.get(i));
@@ -1242,30 +1224,10 @@
         mFailureReasonFile.delete();
     }
 
-    private static class LocalIntentReceiverAsync {
-        final Consumer<Intent> mConsumer;
-
-        LocalIntentReceiverAsync(Consumer<Intent> consumer) {
-            mConsumer = consumer;
-        }
-
-        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
-            @Override
-            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
-                    IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
-                mConsumer.accept(intent);
-            }
-        };
-
-        public IntentSender getIntentSender() {
-            return new IntentSender((IIntentSender) mLocalSender);
-        }
-    }
-
     private static class LocalIntentReceiverSync {
         private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
 
-        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+        private final IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
             @Override
             public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
                     IIntentReceiver finishedReceiver, String requiredPermission,
@@ -1299,6 +1261,20 @@
         return session;
     }
 
+    // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
+    //  verification logic is extracted out of StagingManager into PMS, we can remove
+    //  this.
+    void notifyVerificationComplete(int sessionId) {
+        mPreRebootVerificationHandler.onPreRebootVerificationComplete(sessionId);
+    }
+
+    // TODO(b/136257624): Temporary API to let PMS communicate with StagingManager. When all
+    //  verification logic is extracted out of StagingManager into PMS, we can remove
+    //  this.
+    void notifyPreRebootVerification_Apk_Complete(int sessionId) {
+        mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(sessionId);
+    }
+
     private final class PreRebootVerificationHandler extends Handler {
         // Hold session ids before handler gets ready to do the verification.
         private IntArray mPendingSessionIds;
@@ -1500,54 +1476,16 @@
         }
 
         /**
-         * Pre-reboot verification state for apk files:
-         *   <p><ul>
-         *       <li>performs a dry-run install of apk</li>
-         *   </ul></p>
+         * Pre-reboot verification state for apk files. Session is sent to
+         * {@link PackageManagerService} for verification and it notifies back the result via
+         * {@link #notifyPreRebootVerification_Apk_Complete(int)}
          */
         private void handlePreRebootVerification_Apk(@NonNull PackageInstallerSession session) {
             if (!sessionContainsApk(session)) {
                 notifyPreRebootVerification_Apk_Complete(session.sessionId);
                 return;
             }
-
-            try {
-                Slog.d(TAG, "Running a pre-reboot verification for APKs in session "
-                        + session.sessionId + " by performing a dry-run install");
-                // verifyApksInSession will notify the handler when APK verification is complete
-                verifyApksInSession(session);
-            } catch (PackageManagerException e) {
-                onPreRebootVerificationFailure(session, e.error, e.getMessage());
-            }
-        }
-
-        private void verifyApksInSession(PackageInstallerSession session)
-                throws PackageManagerException {
-
-            final PackageInstallerSession apksToVerify = extractApksInSession(
-                    session,  /* preReboot */ true);
-            if (apksToVerify == null) {
-                return;
-            }
-
-            final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
-                    (Intent result) -> {
-                        final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
-                                PackageInstaller.STATUS_FAILURE);
-                        if (status != PackageInstaller.STATUS_SUCCESS) {
-                            final String errorMessage = result.getStringExtra(
-                                    PackageInstaller.EXTRA_STATUS_MESSAGE);
-                            Slog.e(TAG, "Failure to verify APK staged session "
-                                    + session.sessionId + " [" + errorMessage + "]");
-                            onPreRebootVerificationFailure(session,
-                                    SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage);
-                            return;
-                        }
-                        mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
-                                session.sessionId);
-                    });
-
-            apksToVerify.commit(receiver.getIntentSender(), false);
+            session.verifyStagedSession();
         }
 
         /**
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c1aebd3..d137fd0 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -84,7 +84,6 @@
 import android.security.GateKeeper;
 import android.service.gatekeeper.IGateKeeperService;
 import android.stats.devicepolicy.DevicePolicyEnums;
-import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
@@ -142,6 +141,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Service for {@link UserManager}.
@@ -159,10 +159,6 @@
     private static final String LOG_TAG = "UserManagerService";
     static final boolean DBG = false; // DO NOT SUBMIT WITH TRUE
     private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE
-
-    // TODO(b/164159026): remove once owner_name issue on automotive is fixed
-    // Can be used to track getUsers() / userWithNameLU() behavior
-    public static final boolean DBG_CACHED_USERINFOS = false; // DO NOT SUBMIT WITH TRUE
     // Can be used for manual testing of id recycling
     private static final boolean RELEASE_DELETED_USER_ID = false; // DO NOT SUBMIT WITH TRUE
 
@@ -276,25 +272,6 @@
     private DevicePolicyManagerInternal mDevicePolicyManagerInternal;
 
     /**
-     * Reference to the {@link UserHandle#SYSTEM} user's UserInfo; it's {@code name} was either
-     * manually set, or it's {@code null}.
-     *
-     * <p>The reference is set just once, but it's {@code name} is updated when it's manually set.
-     */
-    @GuardedBy("mUsersLock")
-    private UserInfo mSystemUserInfo;
-
-    /**
-     * Reference to the {@link UserHandle#SYSTEM} user's UserInfo, with its {@code name} set to
-     * the localized value of {@code owner_name}.
-     *
-     * <p>The reference is set just once, but it's {@code name} is updated everytime the reference
-     * is used and the locale changed.
-     */
-    @GuardedBy("mUsersLock")
-    private UserInfo mSystemUserInfoWithName;
-
-    /**
      * Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps.
      */
     @VisibleForTesting
@@ -467,6 +444,11 @@
         }
     };
 
+    // TODO(b/161915546): remove once userWithName() is fixed / removed
+    // Use to debug / dump when user 0 is allocated at userWithName()
+    public static final boolean DBG_ALLOCATION = false; // DO NOT SUBMIT WITH TRUE
+    public final AtomicInteger mUser0Allocations;
+
     /**
      * Start an {@link IntentSender} when user is unlocked after disabling quiet mode.
      *
@@ -656,6 +638,7 @@
         LocalServices.addService(UserManagerInternal.class, mLocalService);
         mLockPatternUtils = new LockPatternUtils(mContext);
         mUserStates.put(UserHandle.USER_SYSTEM, UserState.STATE_BOOTING);
+        mUser0Allocations = DBG_ALLOCATION ? new AtomicInteger() : null;
     }
 
     void systemReady() {
@@ -801,7 +784,7 @@
                         || (excludePreCreated && ui.preCreated)) {
                     continue;
                 }
-                users.add(userWithNameLU(ui));
+                users.add(userWithName(ui));
             }
             return users;
         }
@@ -870,7 +853,7 @@
                 userInfo.name = null;
                 userInfo.iconPath = null;
             } else {
-                userInfo = userWithNameLU(userInfo);
+                userInfo = userWithName(userInfo);
             }
             users.add(userInfo);
         }
@@ -1327,57 +1310,26 @@
     public UserInfo getUserInfo(@UserIdInt int userId) {
         checkManageOrCreateUsersPermission("query user");
         synchronized (mUsersLock) {
-            return userWithNameLU(getUserInfoLU(userId));
+            return userWithName(getUserInfoLU(userId));
         }
     }
 
     /**
      * Returns a UserInfo object with the name filled in, for Owner, or the original
      * if the name is already set.
-     *
-     * <p>Note:</p> the Owner name is localized, so the current value must be checked every time
-     * this method is called.
      */
-    private UserInfo userWithNameLU(UserInfo orig) {
-        // Only the system user uses the owner_name string.
-        if (orig == null || orig.id != UserHandle.USER_SYSTEM) return orig;
-
-        if (mSystemUserInfo == null) {
-            mSystemUserInfo = orig;
-            if (DBG_CACHED_USERINFOS) {
-                Slog.d(LOG_TAG, "Set mSystemUserInfo:" + mSystemUserInfo.toFullString());
+    private UserInfo userWithName(UserInfo orig) {
+        if (orig != null && orig.name == null && orig.id == UserHandle.USER_SYSTEM) {
+            if (DBG_ALLOCATION) {
+                final int number = mUser0Allocations.incrementAndGet();
+                Slog.w(LOG_TAG, "System user instantiated at least " + number + " times");
             }
+            UserInfo withName = new UserInfo(orig);
+            withName.name = getOwnerName();
+            return withName;
+        } else {
+            return orig;
         }
-
-        if (mSystemUserInfo.name != null) {
-            if (DBG_CACHED_USERINFOS) {
-                Slog.v(LOG_TAG, "Returning mSystemUserInfo: " + mSystemUserInfo.toFullString());
-            }
-            return mSystemUserInfo;
-        }
-
-        final String ownerName = getOwnerName();
-
-        if (mSystemUserInfoWithName == null) {
-            mSystemUserInfoWithName = new UserInfo(orig);
-            mSystemUserInfoWithName.name = ownerName;
-            if (DBG_CACHED_USERINFOS) {
-                Slog.d(LOG_TAG, "Set mSystemUserInfoWithName: "
-                        + mSystemUserInfoWithName.toFullString());
-            }
-        } else if (!TextUtils.equals(ownerName, mSystemUserInfoWithName.name)) {
-            if (DBG_CACHED_USERINFOS) {
-                Slog.d(LOG_TAG, "Updating mSystemUserInfoWithName.name from  "
-                        + mSystemUserInfoWithName.name + " to " + ownerName);
-            }
-            mSystemUserInfoWithName.name = ownerName;
-        }
-
-        if (DBG_CACHED_USERINFOS) {
-            Slog.v(LOG_TAG, "Returning mSystemUserInfoWithName:"
-                    + mSystemUserInfoWithName.toFullString());
-        }
-        return mSystemUserInfoWithName;
     }
 
     /** Returns whether the given user type is one of the FULL user types. */
@@ -1530,7 +1482,7 @@
         }
         final int userId = UserHandle.getUserId(Binder.getCallingUid());
         synchronized (mUsersLock) {
-            UserInfo userInfo = userWithNameLU(getUserInfoLU(userId));
+            UserInfo userInfo = userWithName(getUserInfoLU(userId));
             return userInfo == null ? "" : userInfo.name;
         }
     }
@@ -1645,13 +1597,6 @@
             Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId);
             return null;
         }
-
-        if (DBG_CACHED_USERINFOS && userId == UserHandle.USER_SYSTEM && userData != null
-                && userData.info != mSystemUserInfo) {
-            Slog.wtf(LOG_TAG, "getUserInfoLU(): system user on userData (" + userData.info
-                    + ") is not the same as mSystemUserInfo (" + mSystemUserInfo + ")");
-        }
-
         return userData != null ? userData.info : null;
     }
 
@@ -4910,15 +4855,8 @@
         pw.println("  Is headless-system mode: " + UserManager.isHeadlessSystemUserMode());
         pw.println("  User version: " + mUserVersion);
         pw.println("  Owner name: " + getOwnerName());
-        if (mSystemUserInfo == null) {
-            pw.println("  (mSystemUserInfo not set)");
-        } else {
-            pw.println("  System user: " +  mSystemUserInfo.toFullString());
-        }
-        if (mSystemUserInfoWithName == null) {
-            pw.println("  (mSystemUserInfoWithName not set)");
-        } else {
-            pw.println("  System user (with name): " +  mSystemUserInfoWithName.toFullString());
+        if (DBG_ALLOCATION) {
+            pw.println("  System user allocations: " + mUser0Allocations.get());
         }
 
         // Dump UserTypes
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 548cd70..d01a30f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -69,6 +69,7 @@
 import static android.view.WindowManagerGlobal.ADD_OKAY;
 import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
 
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -148,6 +149,7 @@
 import android.os.UserHandle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
+import android.provider.DeviceConfig;
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.service.dreams.DreamManagerInternal;
@@ -1378,12 +1380,14 @@
     }
 
     private long getScreenshotChordLongPressDelay() {
+        long delayMs = DeviceConfig.getLong(
+                DeviceConfig.NAMESPACE_SYSTEMUI, SCREENSHOT_KEYCHORD_DELAY,
+                ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout());
         if (mKeyguardDelegate.isShowing()) {
             // Double the time it takes to take a screenshot from the keyguard
-            return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER *
-                    ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout());
+            return (long) (KEYGUARD_SCREENSHOT_CHORD_DELAY_MULTIPLIER * delayMs);
         }
-        return ViewConfiguration.get(mContext).getScreenshotChordKeyTimeout();
+        return delayMs;
     }
 
     private long getRingerToggleChordDelay() {
@@ -2219,11 +2223,6 @@
     }
 
     @Override
-    public int getMaxWallpaperLayer() {
-        return getWindowLayerFromTypeLw(TYPE_NOTIFICATION_SHADE);
-    }
-
-    @Override
     public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) {
         return attrs.type == TYPE_NOTIFICATION_SHADE;
     }
@@ -5324,15 +5323,6 @@
     }
 
     @Override
-    public boolean isTopLevelWindow(int windowType) {
-        if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
-                && windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
-            return (windowType == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG);
-        }
-        return true;
-    }
-
-    @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         proto.write(ROTATION_MODE, mDefaultDisplayRotation.getUserRotationMode());
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 651eafd..b96d65c 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -67,7 +67,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.WindowConfiguration;
 import android.content.Context;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -90,7 +89,6 @@
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.IShortcutService;
 import com.android.server.wm.DisplayRotation;
-import com.android.server.wm.WindowFrames;
 
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
@@ -181,92 +179,11 @@
      */
     public interface WindowState {
         /**
-         * Return the uid of the app that owns this window.
-         */
-        int getOwningUid();
-
-        /**
          * Return the package name of the app that owns this window.
          */
         String getOwningPackage();
 
         /**
-         * Perform standard frame computation.  The result can be obtained with
-         * getFrame() if so desired.  Must be called with the window manager
-         * lock held.
-         *
-         */
-        public void computeFrameLw();
-
-        /**
-         * Retrieve the current frame of the window that has been assigned by
-         * the window manager.  Must be called with the window manager lock held.
-         *
-         * @return Rect The rectangle holding the window frame.
-         */
-        public Rect getFrameLw();
-
-        /**
-         * Retrieve the frame of the display that this window was last
-         * laid out in.  Must be called with the
-         * window manager lock held.
-         *
-         * @return Rect The rectangle holding the display frame.
-         */
-        public Rect getDisplayFrameLw();
-
-        /**
-         * Retrieve the frame of the content area that this window was last
-         * laid out in.  This is the area in which the content of the window
-         * should be placed.  It will be smaller than the display frame to
-         * account for screen decorations such as a status bar or soft
-         * keyboard.  Must be called with the
-         * window manager lock held.
-         *
-         * @return Rect The rectangle holding the content frame.
-         */
-        public Rect getContentFrameLw();
-
-        /**
-         * Retrieve the frame of the visible area that this window was last
-         * laid out in.  This is the area of the screen in which the window
-         * will actually be fully visible.  It will be smaller than the
-         * content frame to account for transient UI elements blocking it
-         * such as an input method's candidates UI.  Must be called with the
-         * window manager lock held.
-         *
-         * @return Rect The rectangle holding the visible frame.
-         */
-        public Rect getVisibleFrameLw();
-
-        /**
-         * Returns true if this window is waiting to receive its given
-         * internal insets from the client app, and so should not impact the
-         * layout of other windows.
-         */
-        public boolean getGivenInsetsPendingLw();
-
-        /**
-         * Retrieve the insets given by this window's client for the content
-         * area of windows behind it.  Must be called with the
-         * window manager lock held.
-         *
-         * @return Rect The left, top, right, and bottom insets, relative
-         * to the window's frame, of the actual contents.
-         */
-        public Rect getGivenContentInsetsLw();
-
-        /**
-         * Retrieve the insets given by this window's client for the visible
-         * area of windows behind it.  Must be called with the
-         * window manager lock held.
-         *
-         * @return Rect The left, top, right, and bottom insets, relative
-         * to the window's frame, of the actual visible area.
-         */
-        public Rect getGivenVisibleInsetsLw();
-
-        /**
          * Retrieve the current LayoutParams of the window.
          *
          * @return WindowManager.LayoutParams The window's internal LayoutParams
@@ -275,17 +192,6 @@
         public WindowManager.LayoutParams getAttrs();
 
         /**
-         * Retrieve the current system UI visibility flags associated with
-         * this window.
-         */
-        public int getSystemUiVisibility();
-
-        /**
-         * Get the layer at which this window's surface will be Z-ordered.
-         */
-        public int getSurfaceLayer();
-
-        /**
          * Retrieve the type of the top-level window.
          *
          * @return the base type of the parent window if attached or its own type otherwise
@@ -301,22 +207,6 @@
         public IApplicationToken getAppToken();
 
         /**
-         * Return true if this window is participating in voice interaction.
-         */
-        public boolean isVoiceInteraction();
-
-        /**
-         * Return true if, at any point, the application token associated with
-         * this window has actually displayed any windows.  This is most useful
-         * with the "starting up" window to determine if any windows were
-         * displayed when it is closed.
-         *
-         * @return Returns true if one or more windows have been displayed,
-         *         else false.
-         */
-        public boolean hasAppShownWindows();
-
-        /**
          * Is this window visible?  It is not visible if there is no
          * surface, or we are in the process of running an exit animation
          * that will remove the surface.
@@ -324,42 +214,12 @@
         boolean isVisibleLw();
 
         /**
-         * Is this window currently visible to the user on-screen?  It is
-         * displayed either if it is visible or it is currently running an
-         * animation before no longer being visible.  Must be called with the
-         * window manager lock held.
-         */
-        boolean isDisplayedLw();
-
-        /**
          * Return true if this window (or a window it is attached to, but not
          * considering its app token) is currently animating.
          */
         boolean isAnimatingLw();
 
         /**
-         * Is this window considered to be gone for purposes of layout?
-         */
-        boolean isGoneForLayoutLw();
-
-        /**
-         * Returns true if the window has a surface that it has drawn a
-         * complete UI in to. Note that this is different from {@link #hasDrawnLw()}
-         * in that it also returns true if the window is READY_TO_SHOW, but was not yet
-         * promoted to HAS_DRAWN.
-         */
-        boolean isDrawnLw();
-
-        /**
-         * Returns true if this window has been shown on screen at some time in
-         * the past.  Must be called with the window manager lock held.
-         *
-         * @deprecated Use {@link #isDrawnLw} or any of the other drawn/visibility methods.
-         */
-        @Deprecated
-        public boolean hasDrawnLw();
-
-        /**
          * Can be called by the policy to force a window to be hidden,
          * regardless of whether the client or window manager would like
          * it shown.  Must be called with the window manager lock held.
@@ -377,51 +237,12 @@
         public boolean showLw(boolean doAnimation);
 
         /**
-         * Check whether the process hosting this window is currently alive.
-         */
-        public boolean isAlive();
-
-        /**
-         * Check if window is on {@link Display#DEFAULT_DISPLAY}.
-         * @return true if window is on default display.
-         */
-        public boolean isDefaultDisplay();
-
-        /**
          * Check whether the window is currently dimming.
          */
         public boolean isDimming();
 
-        /**
-         * Returns true if the window is letterboxed for the display cutout.
-         */
-        default boolean isLetterboxedForDisplayCutoutLw() {
-            return false;
-        }
-
-        /** @return the current windowing mode of this window. */
-        int getWindowingMode();
-
-        /**
-         * Returns the {@link WindowConfiguration.ActivityType} associated with the configuration
-         * of this window.
-         */
-        default int getActivityType() {
-            return WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-        }
-
-        /**
-         * Returns true if the window is current in multi-windowing mode. i.e. it shares the
-         * screen with other application windows.
-         */
-        boolean inMultiWindowMode();
-
-        public int getRotationAnimationHint();
-
         public boolean isInputMethodWindow();
 
-        public boolean isInputMethodTarget();
-
         public int getDisplayId();
 
         /**
@@ -432,42 +253,8 @@
             return false;
         }
 
-        /**
-         * Returns true if the window owner has the permission to acquire a sleep token when it's
-         * visible. That is, they have the permission {@link Manifest.permission#DEVICE_POWER}.
-         */
-        boolean canAcquireSleepToken();
-
-        /** @return true if this window desires key events. */
-        boolean canReceiveKeys();
-
         /** @return true if the window can show over keyguard. */
         boolean canShowWhenLocked();
-
-        /**
-         * Writes {@link com.android.server.wm.IdentifierProto} to stream.
-         */
-        void writeIdentifierToProto(ProtoOutputStream proto, long fieldId);
-
-        /**
-         * @return The {@link WindowFrames} associated with this {@link WindowState}
-         */
-        WindowFrames getWindowFrames();
-    }
-
-    /**
-     * Representation of a input consumer that the policy has added to the
-     * window manager to consume input events going to windows below it.
-     */
-    public interface InputConsumer {
-        /**
-         * Remove the input consumer from the window manager.
-         */
-        void dismiss();
-        /**
-         * Dispose the input consumer and input receiver from UI thread.
-         */
-        void dispose();
     }
 
     /**
@@ -538,11 +325,6 @@
         void unregisterPointerEventListener(PointerEventListener listener, int displayId);
 
         /**
-         * @return The currently active input method window.
-         */
-        WindowState getInputMethodWindowLw();
-
-        /**
          * Notifies window manager that {@link #isKeyguardTrustedLw} has changed.
          */
         void notifyKeyguardTrustedChanged();
@@ -615,17 +397,6 @@
     }
 
     /**
-     * Provides the rotation of a device.
-     *
-     * @see com.android.server.policy.WindowOrientationListener
-     */
-    public interface RotationSource {
-        int getProposedRotation();
-
-        void setCurrentRotation(int rotation);
-    }
-
-    /**
      * Interface to get public information of a display content.
      */
     public interface DisplayContentInfo {
@@ -889,12 +660,6 @@
     }
 
     /**
-     * Get the highest layer (actually one more than) that the wallpaper is
-     * allowed to be in.
-     */
-    public int getMaxWallpaperLayer();
-
-    /**
      * Return whether the given window can become the Keyguard window. Typically returns true for
      * the StatusBar.
      */
@@ -1384,17 +1149,6 @@
     void dumpDebug(ProtoOutputStream proto, long fieldId);
 
     /**
-     * Returns whether a given window type is considered a top level one.
-     * A top level window does not have a container, i.e. attached window,
-     * or if it has a container it is laid out as a top-level window, not
-     * as a child of its container.
-     *
-     * @param windowType The window type.
-     * @return True if the window is a top level one.
-     */
-    public boolean isTopLevelWindow(int windowType);
-
-    /**
      * Notifies the keyguard to start fading out.
      *
      * @param startTime the start time of the animation in uptime milliseconds
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 8f0fd60..d4375eb 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -94,6 +94,7 @@
     private static final int MSG_USER_ACTIVITY = 1;
     private static final int MSG_BROADCAST = 2;
     private static final int MSG_WIRELESS_CHARGING_STARTED = 3;
+    private static final int MSG_BROADCAST_ENHANCED_PREDICTION = 4;
     private static final int MSG_PROFILE_TIMED_OUT = 5;
     private static final int MSG_WIRED_CHARGING_STARTED = 6;
 
@@ -701,6 +702,16 @@
         mPolicy.userActivity();
     }
 
+    void postEnhancedDischargePredictionBroadcast(long delayMs) {
+        mHandler.sendEmptyMessageDelayed(MSG_BROADCAST_ENHANCED_PREDICTION, delayMs);
+    }
+
+    private void sendEnhancedDischargePredictionBroadcast() {
+        Intent intent = new Intent(PowerManager.ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED)
+                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+    }
+
     private void sendNextBroadcast() {
         final int powerState;
         synchronized (mLock) {
@@ -872,6 +883,10 @@
                 case MSG_WIRELESS_CHARGING_STARTED:
                     showWirelessChargingStarted(msg.arg1, msg.arg2);
                     break;
+                case MSG_BROADCAST_ENHANCED_PREDICTION:
+                    removeMessages(MSG_BROADCAST_ENHANCED_PREDICTION);
+                    sendEnhancedDischargePredictionBroadcast();
+                    break;
                 case MSG_PROFILE_TIMED_OUT:
                     lockProfile(msg.arg1);
                     break;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 882ed1b..9ff164a 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -56,6 +56,7 @@
 import android.os.IPowerManager;
 import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelDuration;
 import android.os.PowerManager;
 import android.os.PowerManager.ServiceType;
 import android.os.PowerManager.WakeData;
@@ -89,11 +90,13 @@
 import android.view.KeyEvent;
 
 import com.android.internal.BrightnessSynchronizer;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
 import com.android.server.LockGuard;
 import com.android.server.RescueParty;
@@ -238,6 +241,18 @@
     private static final int HALT_MODE_REBOOT = 1;
     private static final int HALT_MODE_REBOOT_SAFE_MODE = 2;
 
+    /**
+     * How stale we'll allow the enhanced discharge prediction values to get before considering them
+     * invalid.
+     */
+    private static final long ENHANCED_DISCHARGE_PREDICTION_TIMEOUT_MS = 30 * 60 * 1000L;
+
+    /**
+     * The minimum amount of time between sending consequent
+     * {@link PowerManager#ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED} broadcasts.
+     */
+    private static final long ENHANCED_DISCHARGE_PREDICTION_BROADCAST_MIN_DELAY_MS = 60 * 1000L;
+
     private final Context mContext;
     private final ServiceThread mHandlerThread;
     private final Handler mHandler;
@@ -380,6 +395,34 @@
     // The current battery level percentage.
     private int mBatteryLevel;
 
+    /**
+     * The lock that should be held when interacting with {@link #mEnhancedDischargeTimeElapsed},
+     * {@link #mLastEnhancedDischargeTimeUpdatedElapsed}, and
+     * {@link #mEnhancedDischargePredictionIsPersonalized}.
+     */
+    private final Object mEnhancedDischargeTimeLock = new Object();
+
+    /**
+     * The time (in the elapsed realtime timebase) at which the battery level will reach 0%. This
+     * is provided as an enhanced estimate and only valid if
+     * {@link #mLastEnhancedDischargeTimeUpdatedElapsed} is greater than 0.
+     */
+    @GuardedBy("mEnhancedDischargeTimeLock")
+    private long mEnhancedDischargeTimeElapsed;
+
+    /**
+     * Timestamp (in the elapsed realtime timebase) of last update to enhanced battery estimate
+     * data.
+     */
+    @GuardedBy("mEnhancedDischargeTimeLock")
+    private long mLastEnhancedDischargeTimeUpdatedElapsed;
+
+    /**
+     * Whether or not the current enhanced discharge prediction is personalized to the user.
+     */
+    @GuardedBy("mEnhancedDischargeTimeLock")
+    private boolean mEnhancedDischargePredictionIsPersonalized;
+
     // The battery level percentage at the time the dream started.
     // This is used to terminate a dream and go to sleep if the battery is
     // draining faster than it is charging and the user activity timeout has expired.
@@ -3737,6 +3780,13 @@
             pw.println("  mProximityPositive=" + mProximityPositive);
             pw.println("  mBootCompleted=" + mBootCompleted);
             pw.println("  mSystemReady=" + mSystemReady);
+            synchronized (mEnhancedDischargeTimeLock) {
+                pw.println("  mEnhancedDischargeTimeElapsed=" + mEnhancedDischargeTimeElapsed);
+                pw.println("  mLastEnhancedDischargeTimeUpdatedElapsed="
+                        + mLastEnhancedDischargeTimeUpdatedElapsed);
+                pw.println("  mEnhancedDischargePredictionIsPersonalized="
+                        + mEnhancedDischargePredictionIsPersonalized);
+            }
             pw.println("  mHalAutoSuspendModeEnabled=" + mHalAutoSuspendModeEnabled);
             pw.println("  mHalInteractiveModeEnabled=" + mHalInteractiveModeEnabled);
             pw.println("  mWakeLockSummary=0x" + Integer.toHexString(mWakeLockSummary));
@@ -3952,6 +4002,16 @@
             proto.write(PowerManagerServiceDumpProto.IS_PROXIMITY_POSITIVE, mProximityPositive);
             proto.write(PowerManagerServiceDumpProto.IS_BOOT_COMPLETED, mBootCompleted);
             proto.write(PowerManagerServiceDumpProto.IS_SYSTEM_READY, mSystemReady);
+            synchronized (mEnhancedDischargeTimeLock) {
+                proto.write(PowerManagerServiceDumpProto.ENHANCED_DISCHARGE_TIME_ELAPSED,
+                        mEnhancedDischargeTimeElapsed);
+                proto.write(
+                        PowerManagerServiceDumpProto.LAST_ENHANCED_DISCHARGE_TIME_UPDATED_ELAPSED,
+                        mLastEnhancedDischargeTimeUpdatedElapsed);
+                proto.write(
+                        PowerManagerServiceDumpProto.IS_ENHANCED_DISCHARGE_PREDICTION_PERSONALIZED,
+                        mEnhancedDischargePredictionIsPersonalized);
+            }
             proto.write(
                     PowerManagerServiceDumpProto.IS_HAL_AUTO_SUSPEND_MODE_ENABLED,
                     mHalAutoSuspendModeEnabled);
@@ -3959,7 +4019,8 @@
                     PowerManagerServiceDumpProto.IS_HAL_AUTO_INTERACTIVE_MODE_ENABLED,
                     mHalInteractiveModeEnabled);
 
-            final long activeWakeLocksToken = proto.start(PowerManagerServiceDumpProto.ACTIVE_WAKE_LOCKS);
+            final long activeWakeLocksToken = proto.start(
+                    PowerManagerServiceDumpProto.ACTIVE_WAKE_LOCKS);
             proto.write(
                     PowerManagerServiceDumpProto.ActiveWakeLocksProto.IS_CPU,
                     (mWakeLockSummary & WAKE_LOCK_CPU) != 0);
@@ -4262,6 +4323,7 @@
         if (wcd != null) {
             wcd.dumpDebug(proto, PowerManagerServiceDumpProto.WIRELESS_CHARGER_DETECTOR);
         }
+
         proto.flush();
     }
 
@@ -5022,6 +5084,96 @@
         }
 
         @Override // Binder call
+        public void setBatteryDischargePrediction(@NonNull ParcelDuration timeRemaining,
+                boolean isPersonalized) {
+            // Get current time before acquiring the lock so that the calculated end time is as
+            // accurate as possible.
+            final long nowElapsed = SystemClock.elapsedRealtime();
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+            final long timeRemainingMs = timeRemaining.getDuration().toMillis();
+                // A non-positive number means the battery should be dead right now...
+            Preconditions.checkArgumentPositive(timeRemainingMs,
+                    "Given time remaining is not positive: " + timeRemainingMs);
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    if (mIsPowered) {
+                        throw new IllegalStateException(
+                                "Discharge prediction can't be set while the device is charging");
+                    }
+                }
+
+                final long broadcastDelayMs;
+                synchronized (mEnhancedDischargeTimeLock) {
+                    if (mLastEnhancedDischargeTimeUpdatedElapsed > nowElapsed) {
+                        // Another later call made it into the block first. Keep the latest info.
+                        return;
+                    }
+                    broadcastDelayMs = Math.max(0,
+                            ENHANCED_DISCHARGE_PREDICTION_BROADCAST_MIN_DELAY_MS
+                                    - (nowElapsed - mLastEnhancedDischargeTimeUpdatedElapsed));
+
+                    // No need to persist the discharge prediction values since they'll most likely
+                    // be wrong immediately after a reboot anyway.
+                    mEnhancedDischargeTimeElapsed = nowElapsed + timeRemainingMs;
+                    mEnhancedDischargePredictionIsPersonalized = isPersonalized;
+                    mLastEnhancedDischargeTimeUpdatedElapsed = nowElapsed;
+                }
+                mNotifier.postEnhancedDischargePredictionBroadcast(broadcastDelayMs);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        private boolean isEnhancedDischargePredictionValidLocked(long nowElapsed) {
+            return mLastEnhancedDischargeTimeUpdatedElapsed > 0
+                    && nowElapsed < mEnhancedDischargeTimeElapsed
+                    && nowElapsed - mLastEnhancedDischargeTimeUpdatedElapsed
+                    < ENHANCED_DISCHARGE_PREDICTION_TIMEOUT_MS;
+        }
+
+        @Override // Binder call
+        public ParcelDuration getBatteryDischargePrediction() {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                synchronized (mLock) {
+                    if (mIsPowered) {
+                        return null;
+                    }
+                }
+                synchronized (mEnhancedDischargeTimeLock) {
+                    // Get current time after acquiring the lock so that the calculated duration
+                    // is as accurate as possible.
+                    final long nowElapsed = SystemClock.elapsedRealtime();
+                    if (isEnhancedDischargePredictionValidLocked(nowElapsed)) {
+                        return new ParcelDuration(mEnhancedDischargeTimeElapsed - nowElapsed);
+                    }
+                }
+                return new ParcelDuration(mBatteryStats.computeBatteryTimeRemaining());
+            } catch (RemoteException e) {
+                // Shouldn't happen in-process.
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+            return null;
+        }
+
+        @Override // Binder call
+        public boolean isBatteryDischargePredictionPersonalized() {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                synchronized (mEnhancedDischargeTimeLock) {
+                    return isEnhancedDischargePredictionValidLocked(SystemClock.elapsedRealtime())
+                            && mEnhancedDischargePredictionIsPersonalized;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override // Binder call
         public boolean isDeviceIdleMode() {
             final long ident = Binder.clearCallingIdentity();
             try {
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index 2b793c8..f204aa2 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -52,6 +52,9 @@
     /** The maximum size of signature file.  This is just to avoid potential abuse. */
     private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192;
 
+    /** SHA256 hash size. */
+    private static final int HASH_SIZE_BYTES = 32;
+
     private static final boolean DEBUG = false;
 
     /** Returns true if the given file looks like containing an fs-verity signature. */
@@ -90,8 +93,23 @@
         return (retval == 1);
     }
 
+    /** Returns hash of a root node for the fs-verity enabled file. */
+    public static byte[] getFsverityRootHash(@NonNull String filePath) {
+        byte[] result = new byte[HASH_SIZE_BYTES];
+        int retval = measureFsverityNative(filePath, result);
+        if (retval < 0) {
+            if (retval != -OsConstants.ENODATA) {
+                Slog.e(TAG, "Failed to measure fs-verity, errno " + -retval + ": " + filePath);
+            }
+            return null;
+        }
+        return result;
+    }
+
     private static native int enableFsverityNative(@NonNull String filePath,
             @NonNull byte[] pkcs7Signature);
+    private static native int measureFsverityNative(@NonNull String filePath,
+            @NonNull byte[] digest);
     private static native int statxForFsverityNative(@NonNull String filePath);
 
     /**
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index 6dc1d692..0abeac8 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -336,11 +336,12 @@
     }
 
     /**
-     * Returns {@code true} if {@code vol} is an emulated or public volume,
+     * Returns {@code true} if {@code vol} is an emulated or visible public volume,
      * {@code false} otherwise
      **/
     public static boolean isEmulatedOrPublic(VolumeInfo vol) {
-        return vol.type == VolumeInfo.TYPE_EMULATED || vol.type == VolumeInfo.TYPE_PUBLIC;
+        return vol.type == VolumeInfo.TYPE_EMULATED
+                || (vol.type == VolumeInfo.TYPE_PUBLIC && vol.isVisible());
     }
 
     /** Exception thrown when communication with the {@link ExternalStorageService} fails. */
diff --git a/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java b/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java
new file mode 100644
index 0000000..1500cfa
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/CallerIdentityInjector.java
@@ -0,0 +1,62 @@
+/*
+ * 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.timezonedetector;
+
+import android.annotation.UserIdInt;
+import android.os.Binder;
+import android.os.UserHandle;
+
+/**
+ * An interface to wrap various difficult-to-intercept calls that services make to access / manage
+ * caller identity, e.g. {@link Binder#clearCallingIdentity()}.
+ */
+public interface CallerIdentityInjector {
+
+    /** A singleton for the real implementation of {@link CallerIdentityInjector}. */
+    CallerIdentityInjector REAL = new Real();
+
+    /** A {@link UserHandle#getCallingUserId()} call. */
+    @UserIdInt int getCallingUserId();
+
+    /** A {@link Binder#clearCallingIdentity()} call. */
+    long clearCallingIdentity();
+
+    /** A {@link Binder#restoreCallingIdentity(long)} ()} call. */
+    void restoreCallingIdentity(long token);
+
+    /** The real implementation of {@link CallerIdentityInjector}. */
+    class Real implements CallerIdentityInjector {
+
+        protected Real() {
+        }
+
+        @Override
+        public int getCallingUserId() {
+            return UserHandle.getCallingUserId();
+        }
+
+        @Override
+        public long clearCallingIdentity() {
+            return Binder.clearCallingIdentity();
+        }
+
+        @Override
+        public void restoreCallingIdentity(long token) {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java b/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
copy to services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java
index 114c30e..4c7b1f3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/dagger/PipMenuActivityClass.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationChangeListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright 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.
@@ -14,17 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.pip.phone.dagger;
+package com.android.server.timezonedetector;
 
-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 PipMenuActivityClass {
+/**
+ * A listener used to receive notification that time zone configuration has changed.
+ */
+@FunctionalInterface
+public interface ConfigurationChangeListener {
+    /** Called when the current user or a configuration value has changed. */
+    void onChange();
 }
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
new file mode 100644
index 0000000..aee3d8d
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -0,0 +1,288 @@
+/*
+ * 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.timezonedetector;
+
+import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED;
+import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
+import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED;
+import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.timezonedetector.TimeZoneCapabilities;
+import android.app.timezonedetector.TimeZoneConfiguration;
+
+import java.util.Objects;
+
+/**
+ * Holds all configuration values that affect time zone behavior and some associated logic, e.g.
+ * {@link #getAutoDetectionEnabledBehavior()}, {@link #getGeoDetectionEnabledBehavior()} and {@link
+ * #createCapabilities()}.
+ */
+public final class ConfigurationInternal {
+
+    private final @UserIdInt int mUserId;
+    private final boolean mUserConfigAllowed;
+    private final boolean mAutoDetectionSupported;
+    private final boolean mAutoDetectionEnabled;
+    private final boolean mLocationEnabled;
+    private final boolean mGeoDetectionEnabled;
+
+    private ConfigurationInternal(Builder builder) {
+        mUserId = builder.mUserId;
+        mUserConfigAllowed = builder.mUserConfigAllowed;
+        mAutoDetectionSupported = builder.mAutoDetectionSupported;
+        mAutoDetectionEnabled = builder.mAutoDetectionEnabled;
+        mLocationEnabled = builder.mLocationEnabled;
+        mGeoDetectionEnabled = builder.mGeoDetectionEnabled;
+    }
+
+    /** Returns the ID of the user this configuration is associated with. */
+    public @UserIdInt int getUserId() {
+        return mUserId;
+    }
+
+    /** Returns true if the user allowed to modify time zone configuration. */
+    public boolean isUserConfigAllowed() {
+        return mUserConfigAllowed;
+    }
+
+    /** Returns true if the device supports some form of auto time zone detection. */
+    public boolean isAutoDetectionSupported() {
+        return mAutoDetectionSupported;
+    }
+
+    /** Returns the value of the auto time zone detection enabled setting. */
+    public boolean getAutoDetectionEnabledSetting() {
+        return mAutoDetectionEnabled;
+    }
+
+    /**
+     * Returns true if auto time zone detection behavior is actually enabled, which can be distinct
+     * from the raw setting value. */
+    public boolean getAutoDetectionEnabledBehavior() {
+        return mAutoDetectionSupported && mAutoDetectionEnabled;
+    }
+
+    /** Returns true if user's location can be used generally. */
+    public boolean isLocationEnabled() {
+        return mLocationEnabled;
+    }
+
+    /** Returns the value of the geolocation time zone detection enabled setting. */
+    public boolean getGeoDetectionEnabledSetting() {
+        return mGeoDetectionEnabled;
+    }
+
+    /**
+     * Returns true if geolocation time zone detection behavior is actually enabled, which can be
+     * distinct from the raw setting value.
+     */
+    public boolean getGeoDetectionEnabledBehavior() {
+        if (getAutoDetectionEnabledBehavior()) {
+            return mLocationEnabled && mGeoDetectionEnabled;
+        }
+        return false;
+    }
+
+    /** Creates a {@link TimeZoneCapabilities} object using the configuration values. */
+    public TimeZoneCapabilities createCapabilities() {
+        TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder()
+                .setConfiguration(asConfiguration());
+
+        boolean allowConfigDateTime = isUserConfigAllowed();
+
+        // Automatic time zone detection is only supported on devices if there is a telephony
+        // network available or geolocation time zone detection is possible.
+        boolean deviceHasTimeZoneDetection = isAutoDetectionSupported();
+
+        final int configureAutoDetectionEnabledCapability;
+        if (!deviceHasTimeZoneDetection) {
+            configureAutoDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED;
+        } else if (!allowConfigDateTime) {
+            configureAutoDetectionEnabledCapability = CAPABILITY_NOT_ALLOWED;
+        } else {
+            configureAutoDetectionEnabledCapability = CAPABILITY_POSSESSED;
+        }
+        builder.setConfigureAutoDetectionEnabled(configureAutoDetectionEnabledCapability);
+
+        final int configureGeolocationDetectionEnabledCapability;
+        if (!deviceHasTimeZoneDetection) {
+            configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_SUPPORTED;
+        } else if (!allowConfigDateTime) {
+            configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_ALLOWED;
+        } else if (!isLocationEnabled()) {
+            configureGeolocationDetectionEnabledCapability = CAPABILITY_NOT_APPLICABLE;
+        } else {
+            configureGeolocationDetectionEnabledCapability = CAPABILITY_POSSESSED;
+        }
+        builder.setConfigureGeoDetectionEnabled(configureGeolocationDetectionEnabledCapability);
+
+        // The ability to make manual time zone suggestions can also be restricted by policy. With
+        // the current logic above, this could lead to a situation where a device hardware does not
+        // support auto detection, the device has been forced into "auto" mode by an admin and the
+        // user is unable to disable auto detection.
+        final int suggestManualTimeZoneCapability;
+        if (!allowConfigDateTime) {
+            suggestManualTimeZoneCapability = CAPABILITY_NOT_ALLOWED;
+        } else if (getAutoDetectionEnabledBehavior()) {
+            suggestManualTimeZoneCapability = CAPABILITY_NOT_APPLICABLE;
+        } else {
+            suggestManualTimeZoneCapability = CAPABILITY_POSSESSED;
+        }
+        builder.setSuggestManualTimeZone(suggestManualTimeZoneCapability);
+
+        return builder.build();
+    }
+
+    /** Returns a {@link TimeZoneConfiguration} from the configuration values. */
+    public TimeZoneConfiguration asConfiguration() {
+        return new TimeZoneConfiguration.Builder(mUserId)
+                .setAutoDetectionEnabled(getAutoDetectionEnabledSetting())
+                .setGeoDetectionEnabled(getGeoDetectionEnabledSetting())
+                .build();
+    }
+
+    /**
+     * Merges the configuration values from this with any properties set in {@code
+     * newConfiguration}. The new configuration has precedence. Used to apply user updates to
+     * internal configuration.
+     */
+    public ConfigurationInternal merge(TimeZoneConfiguration newConfiguration) {
+        Builder builder = new Builder(this);
+        if (newConfiguration.hasSetting(TimeZoneConfiguration.SETTING_AUTO_DETECTION_ENABLED)) {
+            builder.setAutoDetectionEnabled(newConfiguration.isAutoDetectionEnabled());
+        }
+        if (newConfiguration.hasSetting(TimeZoneConfiguration.SETTING_GEO_DETECTION_ENABLED)) {
+            builder.setGeoDetectionEnabled(newConfiguration.isGeoDetectionEnabled());
+        }
+        return builder.build();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        ConfigurationInternal that = (ConfigurationInternal) o;
+        return mUserId == that.mUserId
+                && mUserConfigAllowed == that.mUserConfigAllowed
+                && mAutoDetectionSupported == that.mAutoDetectionSupported
+                && mAutoDetectionEnabled == that.mAutoDetectionEnabled
+                && mLocationEnabled == that.mLocationEnabled
+                && mGeoDetectionEnabled == that.mGeoDetectionEnabled;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mUserId, mUserConfigAllowed, mAutoDetectionSupported,
+                mAutoDetectionEnabled, mLocationEnabled, mGeoDetectionEnabled);
+    }
+
+    @Override
+    public String toString() {
+        return "TimeZoneDetectorConfiguration{"
+                + "mUserId=" + mUserId
+                + "mUserConfigAllowed=" + mUserConfigAllowed
+                + "mAutoDetectionSupported=" + mAutoDetectionSupported
+                + "mAutoDetectionEnabled=" + mAutoDetectionEnabled
+                + "mLocationEnabled=" + mLocationEnabled
+                + "mGeoDetectionEnabled=" + mGeoDetectionEnabled
+                + '}';
+    }
+
+    /**
+     * A Builder for {@link ConfigurationInternal}.
+     */
+    public static class Builder {
+
+        private final @UserIdInt int mUserId;
+        private boolean mUserConfigAllowed;
+        private boolean mAutoDetectionSupported;
+        private boolean mAutoDetectionEnabled;
+        private boolean mLocationEnabled;
+        private boolean mGeoDetectionEnabled;
+
+        /**
+         * Creates a new Builder with only the userId set.
+         */
+        public Builder(@UserIdInt int userId) {
+            mUserId = userId;
+        }
+
+        /**
+         * Creates a new Builder by copying values from an existing instance.
+         */
+        public Builder(ConfigurationInternal toCopy) {
+            this.mUserId = toCopy.mUserId;
+            this.mUserConfigAllowed = toCopy.mUserConfigAllowed;
+            this.mAutoDetectionSupported = toCopy.mAutoDetectionSupported;
+            this.mAutoDetectionEnabled = toCopy.mAutoDetectionEnabled;
+            this.mLocationEnabled = toCopy.mLocationEnabled;
+            this.mGeoDetectionEnabled = toCopy.mGeoDetectionEnabled;
+        }
+
+        /**
+         * Sets whether the user is allowed to configure time zone settings on this device.
+         */
+        public Builder setUserConfigAllowed(boolean configAllowed) {
+            mUserConfigAllowed = configAllowed;
+            return this;
+        }
+
+        /**
+         * Sets whether automatic time zone detection is supported on this device.
+         */
+        public Builder setAutoDetectionSupported(boolean supported) {
+            mAutoDetectionSupported = supported;
+            return this;
+        }
+
+        /**
+         * Sets the value of the automatic time zone detection enabled setting for this device.
+         */
+        public Builder setAutoDetectionEnabled(boolean enabled) {
+            mAutoDetectionEnabled = enabled;
+            return this;
+        }
+
+        /**
+         * Sets the value of the location mode setting for this user.
+         */
+        public Builder setLocationEnabled(boolean enabled) {
+            mLocationEnabled = enabled;
+            return this;
+        }
+
+        /**
+         * Sets the value of the geolocation time zone detection setting for this user.
+         */
+        public Builder setGeoDetectionEnabled(boolean enabled) {
+            mGeoDetectionEnabled = enabled;
+            return this;
+        }
+
+        /** Returns a new {@link ConfigurationInternal}. */
+        @NonNull
+        public ConfigurationInternal build() {
+            return new ConfigurationInternal(this);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/TEST_MAPPING b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
new file mode 100644
index 0000000..91e172c
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.timezonedetector."
+        }
+      ]
+    }
+  ]
+}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
index 0ca36e0..d640323 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorCallbackImpl.java
@@ -16,24 +16,30 @@
 
 package com.android.server.timezonedetector;
 
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED;
+import static android.content.Intent.ACTION_USER_SWITCHED;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
-import android.app.timezonedetector.TimeZoneCapabilities;
 import android.app.timezonedetector.TimeZoneConfiguration;
+import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.location.LocationManager;
 import android.net.ConnectivityManager;
+import android.os.Handler;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
 
 import java.util.Objects;
 
@@ -42,103 +48,87 @@
  */
 public final class TimeZoneDetectorCallbackImpl implements TimeZoneDetectorStrategyImpl.Callback {
 
+    private static final String LOG_TAG = "TimeZoneDetectorCallbackImpl";
     private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
 
-    private final Context mContext;
-    private final ContentResolver mCr;
-    private final UserManager mUserManager;
+    @NonNull private final Context mContext;
+    @NonNull private final Handler mHandler;
+    @NonNull private final ContentResolver mCr;
+    @NonNull private final UserManager mUserManager;
+    @NonNull private final boolean mGeoDetectionFeatureEnabled;
+    @NonNull private final LocationManager mLocationManager;
+    // @NonNull after setConfigChangeListener() is called.
+    private ConfigurationChangeListener mConfigChangeListener;
 
-    TimeZoneDetectorCallbackImpl(Context context) {
-        mContext = context;
+    TimeZoneDetectorCallbackImpl(@NonNull Context context, @NonNull Handler handler,
+            boolean geoDetectionFeatureEnabled) {
+        mContext = Objects.requireNonNull(context);
+        mHandler = Objects.requireNonNull(handler);
         mCr = context.getContentResolver();
         mUserManager = context.getSystemService(UserManager.class);
+        mLocationManager = context.getSystemService(LocationManager.class);
+        mGeoDetectionFeatureEnabled = geoDetectionFeatureEnabled;
+
+        // Wire up the change listener. All invocations are performed on the mHandler thread.
+
+        // Listen for the user changing / the user's location mode changing.
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_USER_SWITCHED);
+        filter.addAction(LocationManager.MODE_CHANGED_ACTION);
+        mContext.registerReceiverForAllUsers(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                handleConfigChangeOnHandlerThread();
+            }
+        }, filter, null, mHandler);
+
+        // Add async callbacks for global settings being changed.
+        ContentResolver contentResolver = mContext.getContentResolver();
+        contentResolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
+                new ContentObserver(mHandler) {
+                    public void onChange(boolean selfChange) {
+                        handleConfigChangeOnHandlerThread();
+                    }
+                });
+
+        // Add async callbacks for user scoped location settings being changed.
+        contentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED),
+                true,
+                new ContentObserver(mHandler) {
+                    public void onChange(boolean selfChange) {
+                        handleConfigChangeOnHandlerThread();
+                    }
+                }, UserHandle.USER_ALL);
+    }
+
+    private void handleConfigChangeOnHandlerThread() {
+        if (mConfigChangeListener == null) {
+            Slog.wtf(LOG_TAG, "mConfigChangeListener is unexpectedly null");
+        }
+        mConfigChangeListener.onChange();
     }
 
     @Override
-    public TimeZoneCapabilities getCapabilities(@UserIdInt int userId) {
-        UserHandle userHandle = UserHandle.of(userId);
-        boolean disallowConfigDateTime =
-                mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle);
-
-        TimeZoneCapabilities.Builder builder = new TimeZoneCapabilities.Builder(userId);
-
-        // Automatic time zone detection is only supported (currently) on devices if there is a
-        // telephony network available.
-        if (!deviceHasTelephonyNetwork()) {
-            builder.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_SUPPORTED);
-        } else if (disallowConfigDateTime) {
-            builder.setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED);
-        } else {
-            builder.setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED);
-        }
-
-        // TODO(b/149014708) Replace this with real logic when the settings storage is fully
-        // implemented.
-        builder.setConfigureGeoDetectionEnabled(CAPABILITY_NOT_SUPPORTED);
-
-        // The ability to make manual time zone suggestions can also be restricted by policy. With
-        // the current logic above, this could lead to a situation where a device hardware does not
-        // support auto detection, the device has been forced into "auto" mode by an admin and the
-        // user is unable to disable auto detection.
-        if (disallowConfigDateTime) {
-            builder.setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED);
-        } else if (isAutoDetectionEnabled()) {
-            builder.setSuggestManualTimeZone(CAPABILITY_NOT_APPLICABLE);
-        } else {
-            builder.setSuggestManualTimeZone(CAPABILITY_POSSESSED);
-        }
-        return builder.build();
+    public void setConfigChangeListener(@NonNull ConfigurationChangeListener listener) {
+        mConfigChangeListener = Objects.requireNonNull(listener);
     }
 
     @Override
-    public TimeZoneConfiguration getConfiguration(@UserIdInt int userId) {
-        return new TimeZoneConfiguration.Builder()
+    public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
+        return new ConfigurationInternal.Builder(userId)
+                .setUserConfigAllowed(isUserConfigAllowed(userId))
+                .setAutoDetectionSupported(isAutoDetectionSupported())
                 .setAutoDetectionEnabled(isAutoDetectionEnabled())
-                .setGeoDetectionEnabled(isGeoDetectionEnabled())
+                .setLocationEnabled(isLocationEnabled(userId))
+                .setGeoDetectionEnabled(isGeoDetectionEnabled(userId))
                 .build();
     }
 
     @Override
-    public void setConfiguration(
-            @UserIdInt int userId, @NonNull TimeZoneConfiguration configuration) {
-        Objects.requireNonNull(configuration);
-        if (!configuration.isComplete()) {
-            throw new IllegalArgumentException("configuration=" + configuration + " not complete");
-        }
-
-        // Avoid writing auto detection config for devices that do not support auto time zone
-        // detection: if we wrote it down then we'd set the default explicitly. That might influence
-        // what happens on later releases that do support auto detection on the same hardware.
-        if (isAutoDetectionSupported()) {
-            final int autoEnabledValue = configuration.isAutoDetectionEnabled() ? 1 : 0;
-            Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, autoEnabledValue);
-
-            final boolean geoTzDetectionEnabledValue = configuration.isGeoDetectionEnabled();
-            // TODO(b/149014708) Write this down to user-scoped settings once implemented.
-        }
-    }
-
-    @Override
-    public boolean isAutoDetectionEnabled() {
-        // To ensure that TimeZoneConfiguration is "complete" for simplicity, devices that do not
-        // support auto detection have safe, hard coded configuration values that make it look like
-        // auto detection is turned off. It is therefore important that false is returned from this
-        // method for devices that do not support auto time zone detection. Such devices will not
-        // have a UI to turn the auto detection on/off. Returning true could prevent the user
-        // entering information manually. On devices that do support auto time detection the default
-        // is to turn auto detection on.
-        if (isAutoDetectionSupported()) {
-            return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0;
-        }
-        return false;
-    }
-
-    @Override
-    public boolean isGeoDetectionEnabled() {
-        // TODO(b/149014708) Read this from user-scoped settings once implemented. The user's
-        //  location toggle will act as an override for this setting, i.e. so that the setting will
-        //  return false if the location toggle is disabled.
-        return false;
+    public @UserIdInt int getCurrentUserId() {
+        return LocalServices.getService(ActivityManagerInternal.class).getCurrentUserId();
     }
 
     @Override
@@ -165,8 +155,55 @@
         alarmManager.setTimeZone(zoneId);
     }
 
+    @Override
+    public void storeConfiguration(TimeZoneConfiguration configuration) {
+        Objects.requireNonNull(configuration);
+
+        // Avoid writing the auto detection enabled setting for devices that do not support auto
+        // time zone detection: if we wrote it down then we'd set the value explicitly, which would
+        // prevent detecting "default" later. That might influence what happens on later releases
+        // that support new types of auto detection on the same hardware.
+        if (isAutoDetectionSupported()) {
+            final boolean autoDetectionEnabled = configuration.isAutoDetectionEnabled();
+            setAutoDetectionEnabled(autoDetectionEnabled);
+
+            final int userId = configuration.getUserId();
+            final boolean geoTzDetectionEnabled = configuration.isGeoDetectionEnabled();
+            setGeoDetectionEnabled(userId, geoTzDetectionEnabled);
+        }
+    }
+
+    private boolean isUserConfigAllowed(@UserIdInt int userId) {
+        UserHandle userHandle = UserHandle.of(userId);
+        return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME, userHandle);
+    }
+
     private boolean isAutoDetectionSupported() {
-        return deviceHasTelephonyNetwork();
+        return deviceHasTelephonyNetwork() || mGeoDetectionFeatureEnabled;
+    }
+
+    private boolean isAutoDetectionEnabled() {
+        return Settings.Global.getInt(mCr, Settings.Global.AUTO_TIME_ZONE, 1 /* default */) > 0;
+    }
+
+    private void setAutoDetectionEnabled(boolean enabled) {
+        Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME_ZONE, enabled ? 1 : 0);
+    }
+
+    private boolean isLocationEnabled(@UserIdInt int userId) {
+        return mLocationManager.isLocationEnabledForUser(UserHandle.of(userId));
+    }
+
+    private boolean isGeoDetectionEnabled(@UserIdInt int userId) {
+        final boolean locationEnabled = isLocationEnabled(userId);
+        return Settings.Secure.getIntForUser(mCr,
+                Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
+                locationEnabled ? 1 : 0 /* defaultValue */, userId) != 0;
+    }
+
+    private void setGeoDetectionEnabled(@UserIdInt int userId, boolean enabled) {
+        Settings.Secure.putIntForUser(mCr, Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
+                enabled ? 1 : 0, userId);
     }
 
     private boolean deviceHasTelephonyNetwork() {
@@ -174,4 +211,4 @@
         return mContext.getSystemService(ConnectivityManager.class)
                 .isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
     }
-}
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index fb7a73d..2d50390 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -27,6 +27,12 @@
  */
 public interface TimeZoneDetectorInternal extends Dumpable.Container {
 
+    /** Adds a listener that will be invoked when time zone detection configuration is changed. */
+    void addConfigurationListener(ConfigurationChangeListener listener);
+
+    /** Returns the {@link ConfigurationInternal} for the current user. */
+    ConfigurationInternal getCurrentUserConfigurationInternal();
+
     /**
      * Suggests the current time zone, determined using geolocation, to the detector. The
      * detector may ignore the signal based on system settings, whether better information is
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
index 15412a0..f0ce827 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -20,8 +20,8 @@
 import android.content.Context;
 import android.os.Handler;
 
-import com.android.internal.annotations.VisibleForTesting;
-
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
 /**
@@ -34,18 +34,26 @@
     @NonNull private final Context mContext;
     @NonNull private final Handler mHandler;
     @NonNull private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
+    @NonNull private final List<ConfigurationChangeListener> mConfigurationListeners =
+            new ArrayList<>();
 
-    static TimeZoneDetectorInternalImpl create(@NonNull Context context, @NonNull Handler handler,
-            @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
-        return new TimeZoneDetectorInternalImpl(context, handler, timeZoneDetectorStrategy);
-    }
-
-    @VisibleForTesting
     public TimeZoneDetectorInternalImpl(@NonNull Context context, @NonNull Handler handler,
             @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
         mContext = Objects.requireNonNull(context);
         mHandler = Objects.requireNonNull(handler);
         mTimeZoneDetectorStrategy = Objects.requireNonNull(timeZoneDetectorStrategy);
+
+        // Wire up a change listener so that any downstream listeners can be notified when
+        // the configuration changes for any reason.
+        mTimeZoneDetectorStrategy.addConfigChangeListener(this::handleConfigurationChanged);
+    }
+
+    private void handleConfigurationChanged() {
+        synchronized (mConfigurationListeners) {
+            for (ConfigurationChangeListener listener : mConfigurationListeners) {
+                listener.onChange();
+            }
+        }
     }
 
     @Override
@@ -54,6 +62,19 @@
     }
 
     @Override
+    public void addConfigurationListener(ConfigurationChangeListener listener) {
+        synchronized (mConfigurationListeners) {
+            mConfigurationListeners.add(Objects.requireNonNull(listener));
+        }
+    }
+
+    @Override
+    @NonNull
+    public ConfigurationInternal getCurrentUserConfigurationInternal() {
+        return mTimeZoneDetectorStrategy.getCurrentUserConfigurationInternal();
+    }
+
+    @Override
     public void suggestGeolocationTimeZone(
             @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
         Objects.requireNonNull(timeZoneSuggestion);
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index d81f949..7501d9f 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -24,32 +24,24 @@
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 import android.app.timezonedetector.TimeZoneCapabilities;
 import android.app.timezonedetector.TimeZoneConfiguration;
-import android.content.ContentResolver;
 import android.content.Context;
-import android.database.ContentObserver;
-import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
-import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
-import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.server.FgThread;
 import com.android.server.SystemService;
-import com.android.server.timezonedetector.TimeZoneDetectorStrategy.StrategyListener;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.Objects;
 
 /**
@@ -65,6 +57,9 @@
 
     private static final String TAG = "TimeZoneDetectorService";
 
+    /** A compile time switch for enabling / disabling geolocation-based time zone detection. */
+    private static final boolean GEOLOCATION_TIME_ZONE_DETECTION_ENABLED = false;
+
     /**
      * Handles the service lifecycle for {@link TimeZoneDetectorService} and
      * {@link TimeZoneDetectorInternalImpl}.
@@ -80,18 +75,20 @@
             // Obtain / create the shared dependencies.
             Context context = getContext();
             Handler handler = FgThread.getHandler();
+
             TimeZoneDetectorStrategy timeZoneDetectorStrategy =
-                    TimeZoneDetectorStrategyImpl.create(context);
+                    TimeZoneDetectorStrategyImpl.create(
+                            context, handler, GEOLOCATION_TIME_ZONE_DETECTION_ENABLED);
 
             // Create and publish the local service for use by internal callers.
             TimeZoneDetectorInternal internal =
-                    TimeZoneDetectorInternalImpl.create(context, handler, timeZoneDetectorStrategy);
+                    new TimeZoneDetectorInternalImpl(context, handler, timeZoneDetectorStrategy);
             publishLocalService(TimeZoneDetectorInternal.class, internal);
 
             // Publish the binder service so it can be accessed from other (appropriately
             // permissioned) processes.
-            TimeZoneDetectorService service =
-                    TimeZoneDetectorService.create(context, handler, timeZoneDetectorStrategy);
+            TimeZoneDetectorService service = TimeZoneDetectorService.create(
+                    context, handler, timeZoneDetectorStrategy);
             publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service);
         }
     }
@@ -103,52 +100,38 @@
     private final Handler mHandler;
 
     @NonNull
+    private final CallerIdentityInjector mCallerIdentityInjector;
+
+    @NonNull
     private final TimeZoneDetectorStrategy mTimeZoneDetectorStrategy;
 
-    /**
-     * This sparse array acts as a map from userId to listeners running as that userId. User scoped
-     * as time zone detection configuration is partially user-specific, so different users can
-     * get different configuration.
-     */
     @GuardedBy("mConfigurationListeners")
     @NonNull
-    private final SparseArray<ArrayList<ITimeZoneConfigurationListener>> mConfigurationListeners =
-            new SparseArray<>();
+    private final ArrayList<ITimeZoneConfigurationListener> mConfigurationListeners =
+            new ArrayList<>();
 
     private static TimeZoneDetectorService create(
             @NonNull Context context, @NonNull Handler handler,
             @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
 
-        TimeZoneDetectorService service =
-                new TimeZoneDetectorService(context, handler, timeZoneDetectorStrategy);
-
-        ContentResolver contentResolver = context.getContentResolver();
-        contentResolver.registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE), true,
-                new ContentObserver(handler) {
-                    public void onChange(boolean selfChange) {
-                        service.handleAutoTimeZoneConfigChanged();
-                    }
-                });
-        // TODO(b/149014708) Listen for changes to geolocation time zone detection enabled config.
-        //  This should also include listening to the current user and the current user's location
-        //  toggle since the config is user-scoped and the location toggle overrides the geolocation
-        //  time zone enabled setting.
+        CallerIdentityInjector callerIdentityInjector = CallerIdentityInjector.REAL;
+        TimeZoneDetectorService service = new TimeZoneDetectorService(
+                context, handler, callerIdentityInjector, timeZoneDetectorStrategy);
         return service;
     }
 
     @VisibleForTesting
     public TimeZoneDetectorService(@NonNull Context context, @NonNull Handler handler,
+            @NonNull CallerIdentityInjector callerIdentityInjector,
             @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
         mContext = Objects.requireNonNull(context);
         mHandler = Objects.requireNonNull(handler);
+        mCallerIdentityInjector = Objects.requireNonNull(callerIdentityInjector);
         mTimeZoneDetectorStrategy = Objects.requireNonNull(timeZoneDetectorStrategy);
-        mTimeZoneDetectorStrategy.setStrategyListener(new StrategyListener() {
-            @Override
-            public void onConfigurationChanged() {
-                handleConfigurationChanged();
-            }
-        });
+
+        // Wire up a change listener so that ITimeZoneConfigurationListeners can be notified when
+        // the configuration changes for any reason.
+        mTimeZoneDetectorStrategy.addConfigChangeListener(this::handleConfigurationChanged);
     }
 
     @Override
@@ -156,26 +139,12 @@
     public TimeZoneCapabilities getCapabilities() {
         enforceManageTimeZoneDetectorConfigurationPermission();
 
-        int userId = UserHandle.getCallingUserId();
-        long token = Binder.clearCallingIdentity();
+        int userId = mCallerIdentityInjector.getCallingUserId();
+        long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
-            return mTimeZoneDetectorStrategy.getCapabilities(userId);
+            return mTimeZoneDetectorStrategy.getConfigurationInternal(userId).createCapabilities();
         } finally {
-            Binder.restoreCallingIdentity(token);
-        }
-    }
-
-    @Override
-    @NonNull
-    public TimeZoneConfiguration getConfiguration() {
-        enforceManageTimeZoneDetectorConfigurationPermission();
-
-        int userId = UserHandle.getCallingUserId();
-        long token = Binder.clearCallingIdentity();
-        try {
-            return mTimeZoneDetectorStrategy.getConfiguration(userId);
-        } finally {
-            Binder.restoreCallingIdentity(token);
+            mCallerIdentityInjector.restoreCallingIdentity(token);
         }
     }
 
@@ -184,12 +153,16 @@
         enforceManageTimeZoneDetectorConfigurationPermission();
         Objects.requireNonNull(configuration);
 
-        int userId = UserHandle.getCallingUserId();
-        long token = Binder.clearCallingIdentity();
+        int callingUserId = mCallerIdentityInjector.getCallingUserId();
+        if (callingUserId != configuration.getUserId()) {
+            return false;
+        }
+
+        long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
-            return mTimeZoneDetectorStrategy.updateConfiguration(userId, configuration);
+            return mTimeZoneDetectorStrategy.updateConfiguration(configuration);
         } finally {
-            Binder.restoreCallingIdentity(token);
+            mCallerIdentityInjector.restoreCallingIdentity(token);
         }
     }
 
@@ -197,25 +170,17 @@
     public void addConfigurationListener(@NonNull ITimeZoneConfigurationListener listener) {
         enforceManageTimeZoneDetectorConfigurationPermission();
         Objects.requireNonNull(listener);
-        int userId = UserHandle.getCallingUserId();
 
         synchronized (mConfigurationListeners) {
-            ArrayList<ITimeZoneConfigurationListener> listeners =
-                    mConfigurationListeners.get(userId);
-            if (listeners != null && listeners.contains(listener)) {
+            if (mConfigurationListeners.contains(listener)) {
                 return;
             }
             try {
-                if (listeners == null) {
-                    listeners = new ArrayList<>(1);
-                    mConfigurationListeners.put(userId, listeners);
-                }
-
                 // Ensure the reference to the listener will be removed if the client process dies.
                 listener.asBinder().linkToDeath(this, 0 /* flags */);
 
                 // Only add the listener if we can linkToDeath().
-                listeners.add(listener);
+                mConfigurationListeners.add(listener);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Unable to linkToDeath() for listener=" + listener, e);
             }
@@ -226,19 +191,16 @@
     public void removeConfigurationListener(@NonNull ITimeZoneConfigurationListener listener) {
         enforceManageTimeZoneDetectorConfigurationPermission();
         Objects.requireNonNull(listener);
-        int userId = UserHandle.getCallingUserId();
 
         synchronized (mConfigurationListeners) {
             boolean removedListener = false;
-            ArrayList<ITimeZoneConfigurationListener> userListeners =
-                    mConfigurationListeners.get(userId);
-            if (userListeners.remove(listener)) {
+            if (mConfigurationListeners.remove(listener)) {
                 // Stop listening for the client process to die.
                 listener.asBinder().unlinkToDeath(this, 0 /* flags */);
                 removedListener = true;
             }
             if (!removedListener) {
-                Slog.w(TAG, "Client asked to remove listenener=" + listener
+                Slog.w(TAG, "Client asked to remove listener=" + listener
                         + ", but no listeners were removed."
                         + " mConfigurationListeners=" + mConfigurationListeners);
             }
@@ -259,19 +221,14 @@
     public void binderDied(IBinder who) {
         synchronized (mConfigurationListeners) {
             boolean removedListener = false;
-            final int userCount = mConfigurationListeners.size();
-            for (int i = 0; i < userCount; i++) {
-                ArrayList<ITimeZoneConfigurationListener> userListeners =
-                        mConfigurationListeners.valueAt(i);
-                Iterator<ITimeZoneConfigurationListener> userListenerIterator =
-                        userListeners.iterator();
-                while (userListenerIterator.hasNext()) {
-                    ITimeZoneConfigurationListener userListener = userListenerIterator.next();
-                    if (userListener.asBinder().equals(who)) {
-                        userListenerIterator.remove();
-                        removedListener = true;
-                        break;
-                    }
+            final int listenerCount = mConfigurationListeners.size();
+            for (int listenerIndex = listenerCount - 1; listenerIndex >= 0; listenerIndex--) {
+                ITimeZoneConfigurationListener listener =
+                        mConfigurationListeners.get(listenerIndex);
+                if (listener.asBinder().equals(who)) {
+                    mConfigurationListeners.remove(listenerIndex);
+                    removedListener = true;
+                    break;
                 }
             }
             if (!removedListener) {
@@ -283,42 +240,25 @@
     }
 
     void handleConfigurationChanged() {
-        // Note: we could trigger an async time zone detection operation here via a call to
-        // handleAutoTimeZoneConfigChanged(), but that is triggered in response to the underlying
-        // setting value changing so it is currently unnecessary. If we get to a point where all
-        // configuration changes are guaranteed to happen in response to an updateConfiguration()
-        // call, then we can remove that path and call it here instead.
-
         // Configuration has changed, but each user may have a different view of the configuration.
         // It's possible that this will cause unnecessary notifications but that shouldn't be a
         // problem.
         synchronized (mConfigurationListeners) {
-            final int userCount = mConfigurationListeners.size();
-            for (int userIndex = 0; userIndex < userCount; userIndex++) {
-                int userId = mConfigurationListeners.keyAt(userIndex);
-                TimeZoneConfiguration configuration =
-                        mTimeZoneDetectorStrategy.getConfiguration(userId);
-
-                ArrayList<ITimeZoneConfigurationListener> listeners =
-                        mConfigurationListeners.valueAt(userIndex);
-                final int listenerCount = listeners.size();
-                for (int listenerIndex = 0; listenerIndex < listenerCount; listenerIndex++) {
-                    ITimeZoneConfigurationListener listener = listeners.get(listenerIndex);
-                    try {
-                        listener.onChange(configuration);
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Unable to notify listener=" + listener
-                                + " for userId=" + userId
-                                + " of updated configuration=" + configuration, e);
-                    }
+            final int listenerCount = mConfigurationListeners.size();
+            for (int listenerIndex = 0; listenerIndex < listenerCount; listenerIndex++) {
+                ITimeZoneConfigurationListener listener =
+                        mConfigurationListeners.get(listenerIndex);
+                try {
+                    listener.onChange();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Unable to notify listener=" + listener, e);
                 }
             }
         }
     }
 
     /** Provided for command-line access. This is not exposed as a binder API. */
-    void suggestGeolocationTimeZone(
-            @NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
+    void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion) {
         enforceSuggestGeolocationTimeZonePermission();
         Objects.requireNonNull(timeZoneSuggestion);
 
@@ -331,12 +271,12 @@
         enforceSuggestManualTimeZonePermission();
         Objects.requireNonNull(timeZoneSuggestion);
 
-        int userId = UserHandle.getCallingUserId();
-        long token = Binder.clearCallingIdentity();
+        int userId = mCallerIdentityInjector.getCallingUserId();
+        long token = mCallerIdentityInjector.clearCallingIdentity();
         try {
             return mTimeZoneDetectorStrategy.suggestManualTimeZone(userId, timeZoneSuggestion);
         } finally {
-            Binder.restoreCallingIdentity(token);
+            mCallerIdentityInjector.restoreCallingIdentity(token);
         }
     }
 
@@ -358,12 +298,6 @@
         ipw.flush();
     }
 
-    /** Internal method for handling the auto time zone configuration being changed. */
-    @VisibleForTesting
-    public void handleAutoTimeZoneConfigChanged() {
-        mHandler.post(mTimeZoneDetectorStrategy::handleAutoTimeZoneConfigChanged);
-    }
-
     private void enforceManageTimeZoneDetectorConfigurationPermission() {
         // TODO Switch to a dedicated MANAGE_TIME_AND_ZONE_CONFIGURATION permission.
         mContext.enforceCallingPermission(
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index c5b7e39..f944c56 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -19,51 +19,84 @@
 import android.annotation.UserIdInt;
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
-import android.app.timezonedetector.TimeZoneCapabilities;
 import android.app.timezonedetector.TimeZoneConfiguration;
 import android.util.IndentingPrintWriter;
 
 /**
- * The interface for the class that implements the time detection algorithm used by the
- * {@link TimeZoneDetectorService}.
+ * The interface for the class that is responsible for setting the time zone on a device, used by
+ * {@link TimeZoneDetectorService} and {@link TimeZoneDetectorInternal}.
  *
- * <p>The strategy uses suggestions to decide whether to modify the device's time zone setting
- * and what to set it to.
+ * <p>The strategy receives suggestions, which it may use to modify the device's time zone setting.
+ * Suggestions are acted on or ignored as needed, depending on previously received suggestions and
+ * the current user's configuration (see {@link ConfigurationInternal}).
  *
- * <p>Most calls will be handled by a single thread, but that is not true for all calls. For example
- * {@link #dump(IndentingPrintWriter, String[])}) may be called on a different thread concurrently
- * with other operations so implementations must still handle thread safety.
+ * <p>Devices can have zero, one or two automatic time zone detection algorithm available at any
+ * point in time.
+ *
+ * <p>The two automatic detection algorithms supported are "telephony" and "geolocation". Algorithm
+ * availability and use depends on several factors:
+ * <ul>
+ * <li>Telephony is only available on devices with a telephony stack.
+ * <li>Geolocation is also optional and configured at image creation time. When enabled on a
+ * device, its availability depends on the current user's settings, so switching between users can
+ * change the automatic algorithm used by the device.</li>
+ * </ul>
+ *
+ * <p>If there are no automatic time zone detections algorithms available then the user can usually
+ * change the device time zone manually. Under most circumstances the current user can turn
+ * automatic time zone detection on or off, or choose the algorithm via settings.
+ *
+ * <p>Telephony detection is independent of the current user. The device keeps track of the most
+ * recent telephony suggestion from each slotIndex. When telephony detection is in use, the highest
+ * scoring suggestion is used to set the device time zone based on a scoring algorithm. If several
+ * slotIndexes provide the same score then the slotIndex with the lowest numeric value "wins". If
+ * the situation changes and it is no longer possible to be confident about the time zone,
+ * slotIndexes must have an empty suggestion submitted in order to "withdraw" their previous
+ * suggestion otherwise it will remain in use.
+ *
+ * <p>Geolocation detection is dependent on the current user and their settings. The device retains
+ * at most one geolocation suggestion. Generally, use of a device's location is dependent on the
+ * user's "location toggle", but even when that is enabled the user may choose to enable / disable
+ * the use of geolocation for device time zone detection. If the current user changes to one that
+ * does not have geolocation detection enabled, or the user turns off geolocation detection, then
+ * the strategy discards the latest geolocation suggestion. Devices that lose a location fix must
+ * have an empty suggestion submitted in order to "withdraw" their previous suggestion otherwise it
+ * will remain in use.
+ *
+ * <p>Threading:
+ *
+ * <p>Suggestion calls with a void return type may be handed off to a separate thread and handled
+ * asynchronously. Synchronous calls like {@link #getCurrentUserConfigurationInternal()}, and debug
+ * calls like {@link #dump(IndentingPrintWriter, String[])}, may be called on a different thread
+ * concurrently with other operations.
  *
  * @hide
  */
 public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container {
 
-    /** A listener for strategy events. */
-    interface StrategyListener {
-        /**
-         * Invoked when configuration has been changed.
-         */
-        void onConfigurationChanged();
-    }
+    /**
+     * Sets a listener that will be triggered whenever time zone detection configuration is
+     * changed.
+     */
+    void addConfigChangeListener(@NonNull ConfigurationChangeListener listener);
 
-    /** Sets the listener that enables the strategy to communicate with the surrounding service. */
-    void setStrategyListener(@NonNull StrategyListener listener);
-
-    /** Returns the user's time zone capabilities. */
+    /** Returns the user's time zone configuration. */
     @NonNull
-    TimeZoneCapabilities getCapabilities(@UserIdInt int userId);
+    ConfigurationInternal getConfigurationInternal(@UserIdInt int userId);
 
     /**
-     * Returns the configuration that controls time zone detector behavior.
+     * Returns the configuration that controls time zone detector behavior for the current user.
      */
     @NonNull
-    TimeZoneConfiguration getConfiguration(@UserIdInt int userId);
+    ConfigurationInternal getCurrentUserConfigurationInternal();
 
     /**
-     * Updates the configuration settings that control time zone detector behavior.
+     * Updates the configuration properties that control a device's time zone behavior.
+     *
+     * <p>This method returns {@code true} if the configuration was changed,
+     * {@code false} otherwise.
      */
-    boolean updateConfiguration(
-            @UserIdInt int userId, @NonNull TimeZoneConfiguration configuration);
+    boolean updateConfiguration(@NonNull TimeZoneConfiguration configuration);
 
     /**
      * Suggests zero, one or more time zones for the device, or withdraws a previous suggestion if
@@ -85,9 +118,4 @@
      * suggestion.
      */
     void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion);
-
-    /**
-     * Called when there has been a change to the automatic time zone detection configuration.
-     */
-    void handleAutoTimeZoneConfigChanged();
 }
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index d1369a2..8a42b18 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -20,10 +20,7 @@
 import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
 import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
 import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
 import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED;
-import static android.app.timezonedetector.TimeZoneConfiguration.PROPERTY_AUTO_DETECTION_ENABLED;
-import static android.app.timezonedetector.TimeZoneConfiguration.PROPERTY_GEO_DETECTION_ENABLED;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -33,6 +30,7 @@
 import android.app.timezonedetector.TimeZoneCapabilities;
 import android.app.timezonedetector.TimeZoneConfiguration;
 import android.content.Context;
+import android.os.Handler;
 import android.util.IndentingPrintWriter;
 import android.util.LocalLog;
 import android.util.Slog;
@@ -45,15 +43,7 @@
 import java.util.Objects;
 
 /**
- * An implementation of {@link TimeZoneDetectorStrategy} that handle telephony and manual
- * suggestions. Suggestions are acted on or ignored as needed, dependent on the current "auto time
- * zone detection" setting.
- *
- * <p>For automatic detection, it keeps track of the most recent telephony suggestion from each
- * slotIndex and it uses the best suggestion based on a scoring algorithm. If several slotIndexes
- * provide the same score then the slotIndex with the lowest numeric value "wins". If the situation
- * changes and it is no longer possible to be confident about the time zone, slotIndexes must have
- * an empty suggestion submitted in order to "withdraw" their previous suggestion.
+ * The real implementation of {@link TimeZoneDetectorStrategy}.
  *
  * <p>Most public methods are marked synchronized to ensure thread safety around internal state.
  */
@@ -61,48 +51,27 @@
 
     /**
      * Used by {@link TimeZoneDetectorStrategyImpl} to interact with device configuration / settings
-     * / system properties. It can be faked for testing different scenarios.
+     * / system properties. It can be faked for testing.
      *
      * <p>Note: Because the settings / system properties-derived values can currently be modified
-     * independently and from different threads (and processes!), their use are prone to race
-     * conditions. That will be true until the responsibility for setting their values is moved to
-     * {@link TimeZoneDetectorStrategyImpl} (which is thread safe).
+     * independently and from different threads (and processes!), their use is prone to race
+     * conditions.
      */
     @VisibleForTesting
     public interface Callback {
 
         /**
-         * Returns the capabilities for the user.
+         * Sets a {@link ConfigurationChangeListener} that will be invoked when there are any
+         * changes that could affect time zone detection. This is invoked during system server
+         * setup.
          */
-        @NonNull
-        TimeZoneCapabilities getCapabilities(@UserIdInt int userId);
+        void setConfigChangeListener(@NonNull ConfigurationChangeListener listener);
 
-        /**
-         * Returns the configuration for the user.
-         * @param userId
-         */
-        @NonNull
-        TimeZoneConfiguration getConfiguration(int userId);
+        /** Returns the current user at the instant it is called. */
+        @UserIdInt int getCurrentUserId();
 
-        /**
-         * Sets the configuration for the user. This method handles storage only, the configuration
-         * must have been validated by the caller and be complete.
-         *
-         * @throws IllegalArgumentException if {@link TimeZoneConfiguration#isComplete()}
-         *     returns {@code false}
-         */
-        void setConfiguration(@UserIdInt int userId, @NonNull TimeZoneConfiguration configuration);
-
-        /**
-         * Returns true if automatic time zone detection is currently enabled.
-         */
-        boolean isAutoDetectionEnabled();
-
-        /**
-         * Returns whether geolocation can be used for time zone detection when {@link
-         * #isAutoDetectionEnabled()} returns {@code true}.
-         */
-        boolean isGeoDetectionEnabled();
+        /** Returns the {@link ConfigurationInternal} for the specified user. */
+        ConfigurationInternal getConfigurationInternal(@UserIdInt int userId);
 
         /**
          * Returns true if the device has had an explicit time zone set.
@@ -118,6 +87,13 @@
          * Sets the device's time zone.
          */
         void setDeviceTimeZone(@NonNull String zoneId);
+
+        /**
+         * Stores the configuration properties contained in {@code newConfiguration}.
+         * All checks about user capabilities must be done by the caller and
+         * {@link TimeZoneConfiguration#isComplete()} must be {@code true}.
+         */
+        void storeConfiguration(TimeZoneConfiguration newConfiguration);
     }
 
     private static final String LOG_TAG = "TimeZoneDetectorStrategy";
@@ -189,9 +165,9 @@
     @NonNull
     private final Callback mCallback;
 
-    /** Non-null after {@link #setStrategyListener(StrategyListener)} is called. */
-    @Nullable
-    private StrategyListener mListener;
+    @GuardedBy("this")
+    @NonNull
+    private List<ConfigurationChangeListener> mConfigChangeListeners = new ArrayList<>();
 
     /**
      * A log that records the decisions / decision metadata that affected the device's time zone.
@@ -211,7 +187,8 @@
             new ArrayMapWithHistory<>(KEEP_SUGGESTION_HISTORY_SIZE);
 
     /**
-     * The latest geolocation suggestion received.
+     * The latest geolocation suggestion received. If the user disabled geolocation time zone
+     * detection then the latest suggestion is cleared.
      */
     @GuardedBy("this")
     private ReferenceWithHistory<GeolocationTimeZoneSuggestion> mLatestGeoLocationSuggestion =
@@ -223,113 +200,120 @@
     /**
      * Creates a new instance of {@link TimeZoneDetectorStrategyImpl}.
      */
-    public static TimeZoneDetectorStrategyImpl create(Context context) {
-        Callback timeZoneDetectionServiceHelper = new TimeZoneDetectorCallbackImpl(context);
-        return new TimeZoneDetectorStrategyImpl(timeZoneDetectionServiceHelper);
+    public static TimeZoneDetectorStrategyImpl create(
+            @NonNull Context context, @NonNull Handler handler,
+            boolean geolocationTimeZoneDetectionEnabled) {
+
+        TimeZoneDetectorCallbackImpl callback = new TimeZoneDetectorCallbackImpl(
+                context, handler, geolocationTimeZoneDetectionEnabled);
+        return new TimeZoneDetectorStrategyImpl(callback);
     }
 
     @VisibleForTesting
-    public TimeZoneDetectorStrategyImpl(Callback callback) {
+    public TimeZoneDetectorStrategyImpl(@NonNull Callback callback) {
         mCallback = Objects.requireNonNull(callback);
+        mCallback.setConfigChangeListener(this::handleConfigChanged);
     }
 
     /**
-     * Sets a listener that allows the strategy to communicate with the surrounding service. This
-     * must be called before the instance is used and must only be called once.
+     * Adds a listener that allows the strategy to communicate with the surrounding service /
+     * internal. This must be called before the instance is used.
      */
     @Override
-    public synchronized void setStrategyListener(@NonNull StrategyListener listener) {
-        if (mListener != null) {
-            throw new IllegalStateException("Strategy already has a listener");
-        }
-        mListener = Objects.requireNonNull(listener);
+    public synchronized void addConfigChangeListener(
+            @NonNull ConfigurationChangeListener listener) {
+        Objects.requireNonNull(listener);
+        mConfigChangeListeners.add(listener);
     }
 
     @Override
     @NonNull
-    public synchronized TimeZoneCapabilities getCapabilities(@UserIdInt int userId) {
-        return mCallback.getCapabilities(userId);
+    public ConfigurationInternal getConfigurationInternal(@UserIdInt int userId) {
+        return mCallback.getConfigurationInternal(userId);
     }
 
     @Override
     @NonNull
-    public synchronized TimeZoneConfiguration getConfiguration(@UserIdInt int userId) {
-        return mCallback.getConfiguration(userId);
+    public synchronized ConfigurationInternal getCurrentUserConfigurationInternal() {
+        int currentUserId = mCallback.getCurrentUserId();
+        return getConfigurationInternal(currentUserId);
     }
 
     @Override
     public synchronized boolean updateConfiguration(
-            @UserIdInt int userId, @NonNull TimeZoneConfiguration configurationChanges) {
-        Objects.requireNonNull(configurationChanges);
+            @NonNull TimeZoneConfiguration requestedConfiguration) {
+        Objects.requireNonNull(requestedConfiguration);
 
-        // Validate the requested configuration changes before applying any of them.
-        TimeZoneCapabilities capabilities = mCallback.getCapabilities(userId);
-        boolean canManageTimeZoneDetection =
-                capabilities.getConfigureAutoDetectionEnabled() >= CAPABILITY_NOT_APPLICABLE;
-        if (!canManageTimeZoneDetection
-                && containsAutoTimeDetectionProperties(configurationChanges)) {
+        int userId = requestedConfiguration.getUserId();
+        TimeZoneCapabilities capabilities = getConfigurationInternal(userId).createCapabilities();
+
+        // Create a new configuration builder, and copy across the mutable properties users are
+        // able to modify. Other properties are therefore ignored.
+        final TimeZoneConfiguration newConfiguration =
+                capabilities.applyUpdate(requestedConfiguration);
+        if (newConfiguration == null) {
+            // The changes could not be made due to
             return false;
         }
 
-        // Create a complete configuration by merging the existing and new (possibly partial)
-        // configuration.
-        final TimeZoneConfiguration oldConfiguration = mCallback.getConfiguration(userId);
-        final TimeZoneConfiguration newConfiguration =
-                new TimeZoneConfiguration.Builder(oldConfiguration)
-                        .mergeProperties(configurationChanges)
-                        .build();
+        // Store the configuration / notify as needed. This will cause the mCallback to invoke
+        // handleConfigChanged() asynchronously.
+        mCallback.storeConfiguration(newConfiguration);
 
-        // Set the configuration / notify as needed.
-        boolean configurationChanged = !oldConfiguration.equals(newConfiguration);
-        if (configurationChanged) {
-            mCallback.setConfiguration(userId, newConfiguration);
-
-            String logMsg = "Configuration changed:"
-                    + "oldConfiguration=" + oldConfiguration
-                    + ", configuration=" + configurationChanges
-                    + ", newConfiguration=" + newConfiguration;
-            mTimeZoneChangesLog.log(logMsg);
-            if (DBG) {
-                Slog.d(LOG_TAG, logMsg);
-            }
-            mListener.onConfigurationChanged();
+        TimeZoneConfiguration oldConfiguration = capabilities.getConfiguration();
+        String logMsg = "Configuration changed:"
+                + " oldConfiguration=" + oldConfiguration
+                + ", newConfiguration=" + newConfiguration;
+        mTimeZoneChangesLog.log(logMsg);
+        if (DBG) {
+            Slog.d(LOG_TAG, logMsg);
         }
         return true;
     }
 
-    private static boolean containsAutoTimeDetectionProperties(
-            @NonNull TimeZoneConfiguration configuration) {
-        return configuration.hasProperty(PROPERTY_AUTO_DETECTION_ENABLED)
-                || configuration.hasProperty(PROPERTY_GEO_DETECTION_ENABLED);
-    }
-
     @Override
     public synchronized void suggestGeolocationTimeZone(
             @NonNull GeolocationTimeZoneSuggestion suggestion) {
+
+        int currentUserId = mCallback.getCurrentUserId();
+        ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
         if (DBG) {
-            Slog.d(LOG_TAG, "Geolocation suggestion received. newSuggestion=" + suggestion);
+            Slog.d(LOG_TAG, "Geolocation suggestion received."
+                    + " currentUserConfig=" + currentUserConfig
+                    + " newSuggestion=" + suggestion);
         }
-
         Objects.requireNonNull(suggestion);
-        mLatestGeoLocationSuggestion.set(suggestion);
 
-        // Now perform auto time zone detection. The new suggestion may be used to modify the time
-        // zone setting.
-        if (mCallback.isGeoDetectionEnabled()) {
+        if (currentUserConfig.getGeoDetectionEnabledBehavior()) {
+            // Only store a geolocation suggestion if geolocation detection is currently enabled.
+            mLatestGeoLocationSuggestion.set(suggestion);
+
+            // Now perform auto time zone detection. The new suggestion may be used to modify the
+            // time zone setting.
             String reason = "New geolocation time zone suggested. suggestion=" + suggestion;
-            doAutoTimeZoneDetection(reason);
+            doAutoTimeZoneDetection(currentUserConfig, reason);
         }
     }
 
     @Override
     public synchronized boolean suggestManualTimeZone(
             @UserIdInt int userId, @NonNull ManualTimeZoneSuggestion suggestion) {
+
+        int currentUserId = mCallback.getCurrentUserId();
+        if (userId != currentUserId) {
+            Slog.w(LOG_TAG, "Manual suggestion received but user != current user, userId=" + userId
+                    + " suggestion=" + suggestion);
+
+            // Only listen to changes from the current user.
+            return false;
+        }
+
         Objects.requireNonNull(suggestion);
 
         String timeZoneId = suggestion.getZoneId();
         String cause = "Manual time suggestion received: suggestion=" + suggestion;
 
-        TimeZoneCapabilities capabilities = mCallback.getCapabilities(userId);
+        TimeZoneCapabilities capabilities = getConfigurationInternal(userId).createCapabilities();
         if (capabilities.getSuggestManualTimeZone() != CAPABILITY_POSSESSED) {
             Slog.i(LOG_TAG, "User does not have the capability needed to set the time zone manually"
                     + ", capabilities=" + capabilities
@@ -345,8 +329,12 @@
     @Override
     public synchronized void suggestTelephonyTimeZone(
             @NonNull TelephonyTimeZoneSuggestion suggestion) {
+
+        int currentUserId = mCallback.getCurrentUserId();
+        ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
         if (DBG) {
-            Slog.d(LOG_TAG, "Telephony suggestion received. newSuggestion=" + suggestion);
+            Slog.d(LOG_TAG, "Telephony suggestion received. currentUserConfig=" + currentUserConfig
+                    + " newSuggestion=" + suggestion);
         }
         Objects.requireNonNull(suggestion);
 
@@ -360,9 +348,9 @@
 
         // Now perform auto time zone detection. The new suggestion may be used to modify the time
         // zone setting.
-        if (!mCallback.isGeoDetectionEnabled()) {
+        if (!currentUserConfig.getGeoDetectionEnabledBehavior()) {
             String reason = "New telephony time zone suggested. suggestion=" + suggestion;
-            doAutoTimeZoneDetection(reason);
+            doAutoTimeZoneDetection(currentUserConfig, reason);
         }
     }
 
@@ -392,15 +380,15 @@
      * Performs automatic time zone detection.
      */
     @GuardedBy("this")
-    private void doAutoTimeZoneDetection(@NonNull String detectionReason) {
-        if (!mCallback.isAutoDetectionEnabled()) {
-            // Avoid doing unnecessary work with this (race-prone) check.
+    private void doAutoTimeZoneDetection(
+            @NonNull ConfigurationInternal currentUserConfig, @NonNull String detectionReason) {
+        if (!currentUserConfig.getAutoDetectionEnabledBehavior()) {
+            // Avoid doing unnecessary work.
             return;
         }
 
-        // Use the right suggestions based on the current configuration. This check is potentially
-        // race-prone until this value is set via a call to TimeZoneDetectorStrategy.
-        if (mCallback.isGeoDetectionEnabled()) {
+        // Use the right suggestions based on the current configuration.
+        if (currentUserConfig.getGeoDetectionEnabledBehavior()) {
             doGeolocationTimeZoneDetection(detectionReason);
         } else  {
             doTelephonyTimeZoneDetection(detectionReason);
@@ -480,35 +468,18 @@
 
         // Paranoia: Every suggestion above the SCORE_USAGE_THRESHOLD should have a non-null time
         // zone ID.
-        String newZoneId = bestTelephonySuggestion.suggestion.getZoneId();
-        if (newZoneId == null) {
+        String zoneId = bestTelephonySuggestion.suggestion.getZoneId();
+        if (zoneId == null) {
             Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
                     + " bestTelephonySuggestion=" + bestTelephonySuggestion
                     + " detectionReason=" + detectionReason);
             return;
         }
 
-        String zoneId = bestTelephonySuggestion.suggestion.getZoneId();
         String cause = "Found good suggestion."
                 + ", bestTelephonySuggestion=" + bestTelephonySuggestion
                 + ", detectionReason=" + detectionReason;
-        setAutoDeviceTimeZoneIfRequired(zoneId, cause);
-    }
-
-    @GuardedBy("this")
-    private void setAutoDeviceTimeZoneIfRequired(@NonNull String newZoneId, @NonNull String cause) {
-        Objects.requireNonNull(newZoneId);
-        Objects.requireNonNull(cause);
-
-        if (!mCallback.isAutoDetectionEnabled()) {
-            if (DBG) {
-                Slog.d(LOG_TAG, "Auto time zone detection is not enabled."
-                        + ", newZoneId=" + newZoneId
-                        + ", cause=" + cause);
-            }
-            return;
-        }
-        setDeviceTimeZoneIfRequired(newZoneId, cause);
+        setDeviceTimeZoneIfRequired(zoneId, cause);
     }
 
     @GuardedBy("this")
@@ -582,13 +553,39 @@
         return findBestTelephonySuggestion();
     }
 
-    @Override
-    public synchronized void handleAutoTimeZoneConfigChanged() {
+    private synchronized void handleConfigChanged() {
         if (DBG) {
-            Slog.d(LOG_TAG, "handleAutoTimeZoneConfigChanged()");
+            Slog.d(LOG_TAG, "handleConfigChanged()");
         }
 
-        doAutoTimeZoneDetection("handleAutoTimeZoneConfigChanged()");
+        clearGeolocationSuggestionIfNeeded();
+
+        for (ConfigurationChangeListener listener : mConfigChangeListeners) {
+            listener.onChange();
+        }
+    }
+
+    @GuardedBy("this")
+    private void clearGeolocationSuggestionIfNeeded() {
+        // This method is called whenever the user changes or the config for any user changes. We
+        // don't know what happened, so we capture the current user's config, check to see if we
+        // need to clear state associated with a previous user, and rerun detection.
+        int currentUserId = mCallback.getCurrentUserId();
+        ConfigurationInternal currentUserConfig = mCallback.getConfigurationInternal(currentUserId);
+
+        GeolocationTimeZoneSuggestion latestGeoLocationSuggestion =
+                mLatestGeoLocationSuggestion.get();
+        if (latestGeoLocationSuggestion != null
+                && !currentUserConfig.getGeoDetectionEnabledBehavior()) {
+            // The current user's config has geodetection disabled, so clear the latest suggestion.
+            // This is done to ensure we only ever keep a geolocation suggestion if the user has
+            // said it is ok to do so.
+            mLatestGeoLocationSuggestion.set(null);
+            mTimeZoneChangesLog.log(
+                    "clearGeolocationSuggestionIfNeeded: Cleared latest Geolocation suggestion.");
+        }
+
+        doAutoTimeZoneDetection(currentUserConfig, "clearGeolocationSuggestionIfNeeded()");
     }
 
     @Override
@@ -604,11 +601,14 @@
         ipw.println("TimeZoneDetectorStrategy:");
 
         ipw.increaseIndent(); // level 1
-        ipw.println("mCallback.isAutoDetectionEnabled()=" + mCallback.isAutoDetectionEnabled());
+        int currentUserId = mCallback.getCurrentUserId();
+        ipw.println("mCallback.getCurrentUserId()=" + currentUserId);
+        ConfigurationInternal configuration = mCallback.getConfigurationInternal(currentUserId);
+        ipw.println("mCallback.getConfiguration(currentUserId)=" + configuration);
+        ipw.println("[Capabilities=" + configuration.createCapabilities() + "]");
         ipw.println("mCallback.isDeviceTimeZoneInitialized()="
                 + mCallback.isDeviceTimeZoneInitialized());
         ipw.println("mCallback.getDeviceTimeZone()=" + mCallback.getDeviceTimeZone());
-        ipw.println("mCallback.isGeoDetectionEnabled()=" + mCallback.isGeoDetectionEnabled());
 
         ipw.println("Time zone change log:");
         ipw.increaseIndent(); // level 2
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index a106dc6..0b0bb70 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -258,7 +258,7 @@
         final int callingUid = Binder.getCallingUid();
         final int callingUserId = UserHandle.getUserId(callingUid);
         final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
-        final int packageUid = pm.getPackageUidInternal(packageName,
+        final int packageUid = pm.getPackageUid(packageName,
                 MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, callingUserId);
         if (packageUid != callingUid) {
             throw new SecurityException(
@@ -336,7 +336,7 @@
         if (toPackage != null) {
             mAmInternal.enforceCallingPermission(FORCE_PERSISTABLE_URI_PERMISSIONS,
                     "takePersistableUriPermission");
-            uid = mPmInternal.getPackageUidInternal(toPackage, 0, userId);
+            uid = mPmInternal.getPackageUid(toPackage, 0 /* flags */, userId);
         } else {
             enforceNotIsolatedCaller("takePersistableUriPermission");
             uid = Binder.getCallingUid();
@@ -401,7 +401,7 @@
         if (toPackage != null) {
             mAmInternal.enforceCallingPermission(FORCE_PERSISTABLE_URI_PERMISSIONS,
                     "releasePersistableUriPermission");
-            uid = mPmInternal.getPackageUidInternal(toPackage, 0, userId);
+            uid = mPmInternal.getPackageUid(toPackage, 0 /* flags */ , userId);
         } else {
             enforceNotIsolatedCaller("releasePersistableUriPermission");
             uid = Binder.getCallingUid();
@@ -600,7 +600,7 @@
         if (needed != null) {
             targetUid = needed.targetUid;
         } else {
-            targetUid = mPmInternal.getPackageUidInternal(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
+            targetUid = mPmInternal.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
                     targetUserId);
             if (targetUid < 0) {
                 if (DEBUG) Slog.v(TAG, "Can't grant URI permission no uid for: " + targetPkg
@@ -690,7 +690,7 @@
                         final ProviderInfo pi = getProviderInfo(uri.getAuthority(), sourceUserId,
                                 MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
                         if (pi != null && sourcePkg.equals(pi.packageName)) {
-                            int targetUid = mPmInternal.getPackageUidInternal(
+                            int targetUid = mPmInternal.getPackageUid(
                                         targetPkg, MATCH_UNINSTALLED_PACKAGES, targetUserId);
                             if (targetUid != -1) {
                                 final GrantUri grantUri = new GrantUri(sourceUserId, uri,
@@ -787,7 +787,7 @@
         if (targetPkg == null) {
             throw new NullPointerException("targetPkg");
         }
-        int targetUid = mPmInternal.getPackageUidInternal(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
+        int targetUid = mPmInternal.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
                 targetUserId);
 
         targetUid = checkGrantUriPermissionUnlocked(callingUid, targetPkg, grantUri, modeFlags,
@@ -1108,7 +1108,7 @@
 
         int targetUid = lastTargetUid;
         if (targetUid < 0 && targetPkg != null) {
-            targetUid = mPmInternal.getPackageUidInternal(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
+            targetUid = mPmInternal.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING,
                     UserHandle.getUserId(callingUid));
             if (targetUid < 0) {
                 if (DEBUG) Slog.v(TAG, "Can't grant URI permission no uid for: " + targetPkg);
@@ -1462,7 +1462,8 @@
                     boolean printed = false;
                     int dumpUid = -2;
                     if (dumpPackage != null) {
-                        dumpUid = mPmInternal.getPackageUidInternal(dumpPackage, MATCH_ANY_USER, 0);
+                        dumpUid = mPmInternal.getPackageUid(dumpPackage,
+                                MATCH_ANY_USER, 0 /* userId */);
                     }
                     for (int i = 0; i < mGrantedUriPermissions.size(); i++) {
                         int uid = mGrantedUriPermissions.keyAt(i);
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 9bbeb72..90a153b 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -209,8 +209,8 @@
         private void grantVisibilityToCaller(String webViewPackageName, int callingUid) {
             final PackageManagerInternal pmInternal = LocalServices.getService(
                     PackageManagerInternal.class);
-            final int webviewUid = pmInternal.getPackageUidInternal(
-                    webViewPackageName, 0, UserHandle.getUserId(callingUid));
+            final int webviewUid = pmInternal.getPackageUid(
+                    webViewPackageName, 0 /* flags */, UserHandle.getUserId(callingUid));
             pmInternal.grantImplicitAccess(UserHandle.getUserId(callingUid), null,
                     UserHandle.getAppId(callingUid), webviewUid,
                     true /*direct*/);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 1536473..4c2d0d0 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -700,8 +700,8 @@
                     touchableRegion.getBounds(touchableFrame);
                     RectF windowFrame = mTempRectF;
                     windowFrame.set(touchableFrame);
-                    windowFrame.offset(-windowState.getFrameLw().left,
-                            -windowState.getFrameLw().top);
+                    windowFrame.offset(-windowState.getFrame().left,
+                            -windowState.getFrame().top);
                     matrix.mapRect(windowFrame);
                     Region windowBounds = mTempRegion2;
                     windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
@@ -730,7 +730,7 @@
                     }
 
                     // Count letterbox into nonMagnifiedBounds
-                    if (windowState.isLetterboxedForDisplayCutoutLw()) {
+                    if (windowState.isLetterboxedForDisplayCutout()) {
                         Region letterboxBounds = getLetterboxBounds(windowState);
                         nonMagnifiedBounds.op(letterboxBounds, Region.Op.UNION);
                         availableBounds.op(letterboxBounds, Region.Op.DIFFERENCE);
@@ -1429,11 +1429,11 @@
                         // Account for all space in the task, whether the windows in it are
                         // touchable or not. The modal window blocks all touches from the task's
                         // area.
-                        unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
+                        unaccountedSpace.op(windowState.getDisplayFrame(), unaccountedSpace,
                                 Region.Op.REVERSE_DIFFERENCE);
                     } else {
                         // If a window has tap exclude region, we need to account it.
-                        final Region displayRegion = new Region(windowState.getDisplayFrameLw());
+                        final Region displayRegion = new Region(windowState.getDisplayFrame());
                         final Region tapExcludeRegion = new Region();
                         windowState.getTapExcludeRegion(tapExcludeRegion);
                         displayRegion.op(tapExcludeRegion, displayRegion,
@@ -1470,7 +1470,7 @@
                 // Move to origin as all transforms are captured by the matrix.
                 RectF windowFrame = mTempRectF;
                 windowFrame.set(rect);
-                windowFrame.offset(-windowState.getFrameLw().left, -windowState.getFrameLw().top);
+                windowFrame.offset(-windowState.getFrame().left, -windowState.getFrame().top);
 
                 matrix.mapRect(windowFrame);
 
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9316c46..56261c4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -110,9 +110,13 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS_ANIM;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONTAINERS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SWITCH;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.ActivityRecordProto.ALL_DRAWN;
@@ -145,9 +149,6 @@
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_APP;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SAVED_STATE;
@@ -1096,15 +1097,15 @@
 
     private void scheduleActivityMovedToDisplay(int displayId, Configuration config) {
         if (!attachedToProcess()) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.w(TAG,
-                    "Can't report activity moved to display - client not running, activityRecord="
-                            + this + ", displayId=" + displayId);
+            ProtoLog.w(WM_DEBUG_SWITCH, "Can't report activity moved "
+                    + "to display - client not running, activityRecord=%s, displayId=%d",
+                    this, displayId);
             return;
         }
         try {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
-                    "Reporting activity moved to display" + ", activityRecord=" + this
-                            + ", displayId=" + displayId + ", config=" + config);
+            ProtoLog.v(WM_DEBUG_SWITCH, "Reporting activity moved to "
+                    + "display, activityRecord=%s, displayId=%d, config=%s", this, displayId,
+                    config);
 
             mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     MoveToDisplayItem.obtain(displayId, config));
@@ -1115,14 +1116,13 @@
 
     private void scheduleConfigurationChanged(Configuration config) {
         if (!attachedToProcess()) {
-            if (DEBUG_CONFIGURATION) Slog.w(TAG,
-                    "Can't report activity configuration update - client not running"
-                            + ", activityRecord=" + this);
+            ProtoLog.w(WM_DEBUG_CONFIGURATION, "Can't report activity configuration "
+                    + "update - client not running, activityRecord=%s", this);
             return;
         }
         try {
-            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + this + ", config: "
-                    + config);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending new config to %s, "
+                    + "config: %s", this, config);
 
             mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                     ActivityConfigurationChangeItem.obtain(config));
@@ -1334,7 +1334,7 @@
         if (w == null || winHint != null && w != winHint) {
             return;
         }
-        final boolean surfaceReady = w.isDrawnLw()  // Regular case
+        final boolean surfaceReady = w.isDrawn()  // Regular case
                 || w.mWinAnimator.mSurfaceDestroyDeferred  // The preserved surface is still ready.
                 || w.isDragResizeChanged();  // Waiting for relayoutWindow to call preserveSurface.
         final boolean needsLetterbox = surfaceReady && w.isLetterboxedAppWindow() && fillsParent();
@@ -1355,7 +1355,7 @@
                     : inMultiWindowMode()
                             ? task.getBounds()
                             : getRootTask().getParent().getBounds();
-            mLetterbox.layout(spaceToFill, w.getFrameLw(), mTmpPoint);
+            mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint);
         } else if (mLetterbox != null) {
             mLetterbox.hide();
         }
@@ -1949,10 +1949,8 @@
             startingWindow = null;
             startingDisplayed = false;
             if (surface == null) {
-                ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
-                        "startingWindow was set but startingSurface==null, couldn't "
-                                + "remove");
-
+                ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "startingWindow was set but "
+                        + "startingSurface==null, couldn't remove");
                 return;
             }
         } else {
@@ -1962,9 +1960,10 @@
             return;
         }
 
+
         ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "Schedule remove starting %s startingWindow=%s"
-                        + " startingView=%s Callers=%s",
-                this, startingWindow, startingSurface, Debug.getCallers(5));
+                + " startingView=%s Callers=%s", this, startingWindow, startingSurface,
+                Debug.getCallers(5));
 
 
         // Use the same thread to remove the window as we used to add it, as otherwise we end up
@@ -2399,9 +2398,8 @@
      */
     boolean moveFocusableActivityToTop(String reason) {
         if (!isFocusable()) {
-            if (DEBUG_FOCUS) {
-                Slog.d(TAG_FOCUS, "moveActivityStackToFront: unfocusable activity=" + this);
-            }
+            ProtoLog.d(WM_DEBUG_FOCUS, "moveActivityStackToFront: unfocusable "
+                    + "activity=%s", this);
             return false;
         }
 
@@ -2414,15 +2412,11 @@
 
         if (mRootWindowContainer.getTopResumedActivity() == this
                 && getDisplayContent().mFocusedApp == this) {
-            if (DEBUG_FOCUS) {
-                Slog.d(TAG_FOCUS, "moveActivityStackToFront: already on top, activity=" + this);
-            }
+            ProtoLog.d(WM_DEBUG_FOCUS, "moveActivityStackToFront: already on top, "
+                    + "activity=%s", this);
             return !isState(RESUMED);
         }
-
-        if (DEBUG_FOCUS) {
-            Slog.d(TAG_FOCUS, "moveActivityStackToFront: activity=" + this);
-        }
+        ProtoLog.d(WM_DEBUG_FOCUS, "moveActivityStackToFront: activity=%s", this);
 
         stack.moveToFront(reason, task);
         // Report top activity change to tracking services and WM
@@ -2786,8 +2780,7 @@
         }
         makeFinishingLocked();
 
-        final boolean activityRemoved = destroyImmediately(true /* removeFromApp */,
-                "finish-imm:" + reason);
+        final boolean activityRemoved = destroyImmediately("finish-imm:" + reason);
 
         // If the display does not have running activity, the configuration may need to be
         // updated for restoring original orientation of the display.
@@ -2799,10 +2792,8 @@
             mRootWindowContainer.resumeFocusedStacksTopActivities();
         }
 
-        if (DEBUG_CONTAINERS) {
-            Slog.d(TAG_CONTAINERS, "destroyIfPossible: r=" + this + " destroy returned removed="
-                    + activityRemoved);
-        }
+        ProtoLog.d(WM_DEBUG_CONTAINERS, "destroyIfPossible: r=%s destroy returned "
+                + "removed=%s", this, activityRemoved);
 
         return activityRemoved;
     }
@@ -2835,7 +2826,7 @@
      * @return {@code true} if activity was immediately removed from history, {@code false}
      * otherwise.
      */
-    boolean destroyImmediately(boolean removeFromApp, String reason) {
+    boolean destroyImmediately(String reason) {
         if (DEBUG_SWITCH || DEBUG_CLEANUP) {
             Slog.v(TAG_SWITCH, "Removing activity from " + reason + ": token=" + this
                     + ", app=" + (hasProcess() ? app.mName : "(null)"));
@@ -2857,17 +2848,9 @@
         cleanUp(false /* cleanServices */, false /* setState */);
 
         if (hasProcess()) {
-            if (removeFromApp) {
-                app.removeActivity(this, true /* keepAssociation */);
-                if (!app.hasActivities()) {
-                    mAtmService.clearHeavyWeightProcessIfEquals(app);
-                    // Update any services we are bound to that might care about whether
-                    // their client may have activities.
-                    // No longer have activities, so update LRU list and oom adj.
-                    app.updateProcessInfo(true /* updateServiceConnectionActivities */,
-                            false /* activityChange */, true /* updateOomAdj */,
-                            false /* addPendingTopUid */);
-                }
+            app.removeActivity(this, true /* keepAssociation */);
+            if (!app.hasActivities()) {
+                mAtmService.clearHeavyWeightProcessIfEquals(app);
             }
 
             boolean skipDestroy = false;
@@ -2934,7 +2917,7 @@
                         + " pausing=" + stack.mPausingActivity
                         + " for reason " + reason);
             }
-            return destroyImmediately(true /* removeFromApp */, reason);
+            return destroyImmediately(reason);
         }
         return false;
     }
@@ -2944,10 +2927,9 @@
         finishActivityResults(Activity.RESULT_CANCELED,
                 null /* resultData */, null /* resultGrants */);
         makeFinishingLocked();
-        if (ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE) {
-            Slog.i(TAG_ADD_REMOVE, "Removing activity " + this + " from stack, reason="
-                    + reason + ", callers=" + Debug.getCallers(5));
-        }
+
+        ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing activity %s from stack, reason= %s "
+                        + "callers=%s", this, reason, Debug.getCallers(5));
 
         takeFromHistory();
         removeTimeouts();
@@ -2987,7 +2969,7 @@
     void destroyed(String reason) {
         removeDestroyTimeout();
 
-        if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS, "activityDestroyedLocked: r=" + this);
+        ProtoLog.d(WM_DEBUG_CONTAINERS, "activityDestroyedLocked: r=%s", this);
 
         if (!isState(DESTROYING, DESTROYED)) {
             throw new IllegalStateException(
@@ -3188,12 +3170,9 @@
             remove = false;
         }
         if (remove) {
-            if (ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE || DEBUG_CLEANUP) {
-                Slog.i(TAG_ADD_REMOVE, "Removing activity " + this
-                        + " hasSavedState=" + mHaveState + " stateNotNeeded=" + stateNotNeeded
-                        + " finishing=" + finishing + " state=" + mState
-                        + " callers=" + Debug.getCallers(5));
-            }
+            ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing activity %s hasSavedState=%b "
+                    + "stateNotNeeded=%s finishing=%b state=%s callers=%s", this,
+                    mHaveState, stateNotNeeded, finishing, mState, Debug.getCallers(5));
             if (!finishing || (app != null && app.isRemoved())) {
                 Slog.w(TAG, "Force removing " + this + ": app died, no saved state");
                 EventLogTags.writeWmFinishActivity(mUserId, System.identityHashCode(this),
@@ -4305,7 +4284,7 @@
         } else {
             // If we are being set visible, and the starting window is not yet displayed,
             // then make sure it doesn't get displayed.
-            if (startingWindow != null && !startingWindow.isDrawnLw()) {
+            if (startingWindow != null && !startingWindow.isDrawn()) {
                 startingWindow.clearPolicyVisibilityFlag(LEGACY_POLICY_VISIBILITY);
                 startingWindow.mLegacyPolicyVisibilityAfterAnim = false;
             }
@@ -4487,16 +4466,40 @@
             detachChildren();
         }
 
-        if (state == RESUMED) {
-            mAtmService.updateBatteryStats(this, true);
-            mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_RESUMED);
-        } else if (state == PAUSED) {
-            mAtmService.updateBatteryStats(this, false);
-            mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_PAUSED);
-        } else if (state == STOPPED) {
-            mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_STOPPED);
-        } else if (state == DESTROYED) {
-            mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_DESTROYED);
+        switch (state) {
+            case RESUMED:
+                mAtmService.updateBatteryStats(this, true);
+                mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_RESUMED);
+                // Fall through.
+            case STARTED:
+                // Update process info while making an activity from invisible to visible, to make
+                // sure the process state is updated to foreground.
+                if (app != null) {
+                    app.updateProcessInfo(false /* updateServiceConnectionActivities */,
+                            true /* activityChange */, true /* updateOomAdj */,
+                            true /* addPendingTopUid */);
+                }
+                break;
+            case PAUSED:
+                mAtmService.updateBatteryStats(this, false);
+                mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_PAUSED);
+                break;
+            case STOPPED:
+                mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_STOPPED);
+                break;
+            case DESTROYED:
+                mAtmService.updateActivityUsageStats(this, Event.ACTIVITY_DESTROYED);
+                // Fall through.
+            case DESTROYING:
+                if (app != null && !app.hasActivities()) {
+                    // Update any services we are bound to that might care about whether
+                    // their client may have activities.
+                    // No longer have activities, so update LRU list and oom adj.
+                    app.updateProcessInfo(true /* updateServiceConnectionActivities */,
+                            false /* activityChange */, true /* updateOomAdj */,
+                            false /* addPendingTopUid */);
+                }
+                break;
         }
     }
 
@@ -4807,14 +4810,6 @@
             }
             setState(STARTED, "makeActiveIfNeeded");
 
-            // Update process info while making an activity from invisible to visible, to make
-            // sure the process state is updated to foreground.
-            if (app != null) {
-                app.updateProcessInfo(false /* updateServiceConnectionActivities */,
-                        true /* activityChange */, true /* updateOomAdj */,
-                        true /* addPendingTopUid */);
-            }
-
             try {
                 mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
                         StartActivityItem.obtain());
@@ -5122,7 +5117,7 @@
             if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + this);
             setState(STOPPED, "stopIfPossible");
             if (deferRelaunchUntilPaused) {
-                destroyImmediately(true /* removeFromApp */, "stop-except");
+                destroyImmediately("stop-except");
             }
         }
     }
@@ -5163,7 +5158,7 @@
                 clearOptionsLocked();
             } else {
                 if (deferRelaunchUntilPaused) {
-                    destroyImmediately(true /* removeFromApp */, "stop-config");
+                    destroyImmediately("stop-config");
                     mRootWindowContainer.resumeFocusedStacksTopActivities();
                 } else {
                     mRootWindowContainer.updatePreviousProcess(this);
@@ -5589,9 +5584,9 @@
             if (DEBUG_VISIBILITY || WM_DEBUG_ORIENTATION.isLogToLogcat()) {
                 final boolean isAnimationSet = isAnimating(TRANSITION | PARENTS,
                         ANIMATION_TYPE_APP_TRANSITION);
-                Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
+                Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawn()
                         + ", isAnimationSet=" + isAnimationSet);
-                if (!w.isDrawnLw()) {
+                if (!w.isDrawn()) {
                     Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
                             + " pv=" + w.isVisibleByPolicy()
                             + " mDrawState=" + winAnimator.drawStateToString()
@@ -5606,7 +5601,7 @@
                     if (findMainWindow(false /* includeStartingApp */) != w) {
                         mNumInterestingWindows++;
                     }
-                    if (w.isDrawnLw()) {
+                    if (w.isDrawn()) {
                         mNumDrawnWindows++;
 
                         if (DEBUG_VISIBILITY || WM_DEBUG_ORIENTATION.isLogToLogcat()) {
@@ -5619,7 +5614,7 @@
                         isInterestingAndDrawn = true;
                     }
                 }
-            } else if (w.isDrawnLw()) {
+            } else if (w.isDrawn()) {
                 // The starting window for this container is drawn.
                 mStackSupervisor.getActivityMetricsLogger().notifyStartingWindowDrawn(this);
                 startingDisplayed = true;
@@ -6148,7 +6143,7 @@
         if (win == null) {
             return;
         }
-        final Rect frame = win.getRelativeFrameLw();
+        final Rect frame = win.getRelativeFrame();
         final int thumbnailDrawableRes = task.mUserId == mWmService.mCurrentUserId
                 ? R.drawable.ic_account_circle
                 : R.drawable.ic_corp_badge;
@@ -6174,7 +6169,7 @@
         // destination of the thumbnail header animation. If this is a full screen
         // window scenario, we use the whole display as the target.
         WindowState win = findMainWindow();
-        Rect appRect = win != null ? win.getContentFrameLw() :
+        final Rect appRect = win != null ? win.getContentFrame() :
                 new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
         final Rect insets = win != null ? win.getContentInsets() : null;
         final Configuration displayConfig = mDisplayContent.getConfiguration();
@@ -7000,27 +6995,27 @@
             boolean ignoreVisibility) {
         final Task stack = getRootTask();
         if (stack.mConfigWillChange) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Skipping config check (will change): " + this);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check "
+                    + "(will change): %s", this);
             return true;
         }
 
         // We don't worry about activities that are finishing.
         if (finishing) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Configuration doesn't matter in finishing " + this);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration doesn't matter "
+                    + "in finishing %s", this);
             stopFreezingScreenLocked(false);
             return true;
         }
 
         if (!ignoreVisibility && (mState == STOPPING || mState == STOPPED || !shouldBeVisible())) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Skipping config check invisible: " + this);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check "
+                    + "invisible: %s", this);
             return true;
         }
 
-        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                "Ensuring correct configuration: " + this);
+        ProtoLog.v(WM_DEBUG_CONFIGURATION, "Ensuring correct "
+                + "configuration: %s", this);
 
         final int newDisplayId = getDisplayId();
         final boolean displayChanged = mLastReportedDisplayId != newDisplayId;
@@ -7036,8 +7031,8 @@
         // the combine configurations are equal, but would otherwise differ in the override config
         mTmpConfig.setTo(mLastReportedConfiguration.getMergedConfiguration());
         if (getConfiguration().equals(mTmpConfig) && !forceNewConfig && !displayChanged) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Configuration & display unchanged in " + this);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration & display "
+                    + "unchanged in %s", this);
             return true;
         }
 
@@ -7057,14 +7052,14 @@
             // No need to relaunch or schedule new config for activity that hasn't been launched
             // yet. We do, however, return after applying the config to activity record, so that
             // it will use it for launch transaction.
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Skipping config check for initializing activity: " + this);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Skipping config check for "
+                    + "initializing activity: %s", this);
             return true;
         }
 
         if (changes == 0 && !forceNewConfig) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Configuration no differences in " + this);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration no differences in %s",
+                    this);
             // There are no significant differences, so we won't relaunch but should still deliver
             // the new configuration to the client process.
             if (displayChanged) {
@@ -7075,26 +7070,23 @@
             return true;
         }
 
-        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                "Configuration changes for " + this + ", allChanges="
-                        + Configuration.configurationDiffToString(changes));
+        ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration changes for %s, "
+                + "allChanges=%s", this, Configuration.configurationDiffToString(changes));
 
         // If the activity isn't currently running, just leave the new configuration and it will
         // pick that up next time it starts.
         if (!attachedToProcess()) {
-            if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                    "Configuration doesn't matter not running " + this);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration doesn't matter not running %s", this);
             stopFreezingScreenLocked(false);
             forceNewConfig = false;
             return true;
         }
 
         // Figure out how to handle the changes between the configurations.
-        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                "Checking to restart " + info.name + ": changed=0x"
-                        + Integer.toHexString(changes) + ", handles=0x"
-                        + Integer.toHexString(info.getRealConfigChanged())
-                        + ", mLastReportedConfiguration=" + mLastReportedConfiguration);
+        ProtoLog.v(WM_DEBUG_CONFIGURATION, "Checking to restart %s: changed=0x%s, "
+                + "handles=0x%s, mLastReportedConfiguration=%s", info.name,
+                Integer.toHexString(changes), Integer.toHexString(info.getRealConfigChanged()),
+                mLastReportedConfiguration);
 
         if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
             // Aha, the activity isn't handling the change, so DIE DIE DIE.
@@ -7111,20 +7103,20 @@
                 mRelaunchReason = RELAUNCH_REASON_NONE;
             }
             if (!attachedToProcess()) {
-                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                        "Config is destroying non-running " + this);
-                destroyImmediately(true /* removeFromApp */, "config");
+                ProtoLog.v(WM_DEBUG_CONFIGURATION,
+                        "Config is destroying non-running %s", this);
+                destroyImmediately("config");
             } else if (mState == PAUSING) {
                 // A little annoying: we are waiting for this activity to finish pausing. Let's not
                 // do anything now, but just flag that it needs to be restarted when done pausing.
-                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                        "Config is skipping already pausing " + this);
+                ProtoLog.v(WM_DEBUG_CONFIGURATION,
+                        "Config is skipping already pausing %s", this);
                 deferRelaunchUntilPaused = true;
                 preserveWindowOnDeferredRelaunch = preserveWindow;
                 return true;
             } else {
-                if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
-                        "Config is relaunching " + this);
+                ProtoLog.v(WM_DEBUG_CONFIGURATION, "Config is relaunching %s",
+                        this);
                 if (DEBUG_STATES && !mVisibleRequested) {
                     Slog.v(TAG_STATES, "Config is relaunching invisible activity " + this
                             + " called by " + Debug.getCallers(4));
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 2c475e0..34f7f79 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -41,10 +41,8 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
 import static android.os.Process.INVALID_UID;
-import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.Display.TYPE_VIRTUAL;
 
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
@@ -1869,7 +1867,7 @@
         for (int i = 0; i < numFinishingActivities; i++) {
             final ActivityRecord r = finishingActivities.get(i);
             if (r.isInHistory()) {
-                r.destroyImmediately(true /* removeFromApp */, "finish-" + reason);
+                r.destroyImmediately("finish-" + reason);
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index f615838..f206259 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -56,12 +56,12 @@
 import static android.os.Process.INVALID_UID;
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
@@ -116,6 +116,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
 import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.pm.InstantAppResolver;
 import com.android.server.power.ShutdownCheckPoints;
@@ -669,10 +670,8 @@
                 if (stack != null) {
                     stack.mConfigWillChange = globalConfigWillChange;
                 }
-                if (DEBUG_CONFIGURATION) {
-                    Slog.v(TAG_CONFIGURATION, "Starting activity when config will change = "
-                            + globalConfigWillChange);
-                }
+                ProtoLog.v(WM_DEBUG_CONFIGURATION, "Starting activity when config "
+                        + "will change = %b", globalConfigWillChange);
 
                 final long origId = Binder.clearCallingIdentity();
 
@@ -695,10 +694,9 @@
                     if (stack != null) {
                         stack.mConfigWillChange = false;
                     }
-                    if (DEBUG_CONFIGURATION) {
-                        Slog.v(TAG_CONFIGURATION,
+                    ProtoLog.v(WM_DEBUG_CONFIGURATION,
                                 "Updating to new configuration after starting activity.");
-                    }
+
                     mService.updateConfigurationLocked(mRequest.globalConfig, null, false);
                 }
 
@@ -1693,8 +1691,9 @@
             // we need to resolve resultTo to a uid as grantImplicitAccess deals explicitly in UIDs
             final PackageManagerInternal pmInternal =
                     mService.getPackageManagerInternalLocked();
-            final int resultToUid = pmInternal.getPackageUidInternal(
-                            mStartActivity.resultTo.info.packageName, 0, mStartActivity.mUserId);
+            final int resultToUid = pmInternal.getPackageUid(
+                    mStartActivity.resultTo.info.packageName, 0 /* flags */,
+                    mStartActivity.mUserId);
             pmInternal.grantImplicitAccess(mStartActivity.mUserId, mIntent,
                     UserHandle.getAppId(mStartActivity.info.applicationInfo.uid) /*recipient*/,
                     resultToUid /*visible*/, true /*direct*/);
@@ -1711,8 +1710,9 @@
         mRootWindowContainer.startPowerModeLaunchIfNeeded(
                 false /* forceSend */, mStartActivity);
 
-        mTargetStack.startActivityLocked(mStartActivity, topStack.getTopNonFinishingActivity(),
-                newTask, mKeepCurTransition, mOptions);
+        mTargetStack.startActivityLocked(mStartActivity,
+                topStack != null ? topStack.getTopNonFinishingActivity() : null, newTask,
+                mKeepCurTransition, mOptions);
         if (mDoResume) {
             final ActivityRecord topTaskActivity =
                     mStartActivity.getTask().topRunningActivityLocked();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java b/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java
index da0bfd6..3c562a6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerDebugConfig.java
@@ -43,12 +43,6 @@
     // Enable all debug log categories for activities.
     private static final boolean DEBUG_ALL_ACTIVITIES = DEBUG_ALL || false;
 
-    static final boolean DEBUG_ADD_REMOVE = DEBUG_ALL_ACTIVITIES || false;
-    public static final boolean DEBUG_CONFIGURATION = DEBUG_ALL || false;
-    static final boolean DEBUG_CONTAINERS = DEBUG_ALL_ACTIVITIES || false;
-    static final boolean DEBUG_FOCUS = false;
-    static final boolean DEBUG_IMMERSIVE = DEBUG_ALL || false;
-    static final boolean DEBUG_LOCKTASK = DEBUG_ALL || false;
     static final boolean DEBUG_PAUSE = DEBUG_ALL || false;
     static final boolean DEBUG_RECENTS = DEBUG_ALL || false;
     static final boolean DEBUG_RECENTS_TRIM_TASKS = DEBUG_RECENTS || false;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 403f225..6a8cbfb 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -66,6 +66,10 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.TRANSIT_NONE;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
 import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR;
 import static com.android.server.am.ActivityManagerService.MY_PID;
 import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
@@ -92,10 +96,6 @@
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_IMMERSIVE;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
@@ -242,6 +242,7 @@
 import com.android.internal.os.TransferPipe;
 import com.android.internal.policy.IKeyguardDismissCallback;
 import com.android.internal.policy.KeyguardDismissCallback;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FrameworkStatsLog;
@@ -813,7 +814,7 @@
             // in-place.
             updateConfigurationLocked(configuration, null, true);
             final Configuration globalConfig = getGlobalConfiguration();
-            if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Initial config: " + globalConfig);
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Initial config: %s", globalConfig);
 
             // Load resources only after the current configuration has been set.
             final Resources res = mContext.getResources();
@@ -1960,7 +1961,7 @@
 
             // update associated state if we're frontmost
             if (r.isFocusedActivityOnDisplay()) {
-                if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE, "Frontmost changed immersion: "+ r);
+                ProtoLog.d(WM_DEBUG_IMMERSIVE, "Frontmost changed immersion: %s", r);
                 applyUpdateLockStateLocked(r);
             }
         }
@@ -1974,8 +1975,8 @@
         final boolean nextState = r != null && r.immersive;
         mH.post(() -> {
             if (mUpdateLock.isHeld() != nextState) {
-                if (DEBUG_IMMERSIVE) Slog.d(TAG_IMMERSIVE,
-                        "Applying new update lock state '" + nextState + "' for " + r);
+                ProtoLog.d(WM_DEBUG_IMMERSIVE, "Applying new update lock state '%s' for %s",
+                        nextState, r);
                 if (nextState) {
                     mUpdateLock.acquire();
                 } else {
@@ -2176,7 +2177,7 @@
     @Override
     public void setFocusedStack(int stackId) {
         mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedStack()");
-        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedStack: stackId=" + stackId);
+        ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedStack: stackId=%d", stackId);
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -2198,7 +2199,7 @@
     @Override
     public void setFocusedTask(int taskId) {
         mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "setFocusedTask()");
-        if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedTask: taskId=" + taskId);
+        ProtoLog.d(WM_DEBUG_FOCUS, "setFocusedTask: taskId=%d", taskId);
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -3013,7 +3014,7 @@
     }
 
     private void startLockTaskModeLocked(@Nullable Task task, boolean isSystemCaller) {
-        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "startLockTaskModeLocked: " + task);
+        ProtoLog.w(WM_DEBUG_LOCKTASK, "startLockTaskModeLocked: %s", task);
         if (task == null || task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
             return;
         }
@@ -3075,8 +3076,7 @@
                     "updateLockTaskPackages()");
         }
         synchronized (mGlobalLock) {
-            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowlisting " + userId + ":"
-                    + Arrays.toString(packages));
+            ProtoLog.w(WM_DEBUG_LOCKTASK, "Allowlisting %d:%s", userId, Arrays.toString(packages));
             getLockTaskController().updateLockTaskPackages(userId, packages);
         }
     }
@@ -3379,7 +3379,7 @@
                 if (r == null || !r.isDestroyable()) {
                     return false;
                 }
-                r.destroyImmediately(true /* removeFromApp */, "app-req");
+                r.destroyImmediately("app-req");
                 return r.isState(DESTROYING, DESTROYED);
             } finally {
                 Binder.restoreCallingIdentity(origId);
@@ -4001,9 +4001,9 @@
     @Override
     public void reportSizeConfigurations(IBinder token, int[] horizontalSizeConfiguration,
             int[] verticalSizeConfigurations, int[] smallestSizeConfigurations) {
-        if (DEBUG_CONFIGURATION) Slog.v(TAG, "Report configuration: " + token + " "
-                + Arrays.toString(horizontalSizeConfiguration) + " "
-                + Arrays.toString(verticalSizeConfigurations));
+        ProtoLog.v(WM_DEBUG_CONFIGURATION, "Report configuration: %s %s %s",
+                token, Arrays.toString(horizontalSizeConfiguration),
+                Arrays.toString(verticalSizeConfigurations));
         synchronized (mGlobalLock) {
             ActivityRecord record = ActivityRecord.isInStackLocked(token);
             if (record == null) {
@@ -4497,8 +4497,8 @@
                     "updateLockTaskFeatures()");
         }
         synchronized (mGlobalLock) {
-            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Allowing features " + userId + ":0x" +
-                    Integer.toHexString(flags));
+            ProtoLog.w(WM_DEBUG_LOCKTASK, "Allowing features %d:0x%s",
+                    userId, Integer.toHexString(flags));
             getLockTaskController().updateLockTaskFeatures(userId, flags);
         }
     }
@@ -5183,8 +5183,8 @@
             return 0;
         }
 
-        if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.i(TAG_CONFIGURATION,
-                "Updating global configuration to: " + values);
+        ProtoLog.i(WM_DEBUG_CONFIGURATION, "Updating global configuration "
+                + "to: %s", values);
         writeConfigurationChanged(changes);
         FrameworkStatsLog.write(FrameworkStatsLog.RESOURCE_CONFIGURATION_CHANGED,
                 values.colorMode,
@@ -5262,10 +5262,8 @@
         for (int i = pidMap.size() - 1; i >= 0; i--) {
             final int pid = pidMap.keyAt(i);
             final WindowProcessController app = pidMap.get(pid);
-            if (DEBUG_CONFIGURATION) {
-                Slog.v(TAG_CONFIGURATION, "Update process config of "
-                        + app.mName + " to new config " + configCopy);
-            }
+            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Update process config of %s to new "
+                    + "config %s", app.mName, configCopy);
             app.onConfigurationChanged(configCopy);
         }
 
@@ -6563,10 +6561,8 @@
             if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) return;
 
             if (pid == MY_PID || pid < 0) {
-                if (DEBUG_CONFIGURATION) {
-                    Slog.w(TAG,
+                ProtoLog.w(WM_DEBUG_CONFIGURATION,
                             "Trying to update display configuration for system/invalid process.");
-                }
                 return;
             }
             synchronized (mGlobalLock) {
@@ -6574,18 +6570,14 @@
                         mRootWindowContainer.getDisplayContent(displayId);
                 if (displayContent == null) {
                     // Call might come when display is not yet added or has been removed.
-                    if (DEBUG_CONFIGURATION) {
-                        Slog.w(TAG, "Trying to update display configuration for non-existing "
-                                + "displayId=" + displayId);
-                    }
+                    ProtoLog.w(WM_DEBUG_CONFIGURATION, "Trying to update display "
+                            + "configuration for non-existing displayId=%d", displayId);
                     return;
                 }
                 final WindowProcessController process = mProcessMap.getProcess(pid);
                 if (process == null) {
-                    if (DEBUG_CONFIGURATION) {
-                        Slog.w(TAG, "Trying to update display configuration for invalid "
-                                + "process, pid=" + pid);
-                    }
+                    ProtoLog.w(WM_DEBUG_CONFIGURATION, "Trying to update display "
+                            + "configuration for invalid process, pid=%d", pid);
                     return;
                 }
                 process.registerDisplayConfigurationListener(displayContent);
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index c144755..8568d5f 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -222,12 +222,12 @@
     }
 
     protected boolean skipAnimation() {
-        return !mWin.isDrawnLw();
+        return !mWin.isDrawn();
     }
 
     private @StatusBarManager.WindowVisibleState int computeStateLw(
             boolean wasVis, boolean wasAnim, WindowState win, boolean change) {
-        if (win.isDrawnLw()) {
+        if (win.isDrawn()) {
             final boolean vis = win.isVisibleLw();
             final boolean anim = win.isAnimatingLw();
             if (mState == StatusBarManager.WINDOW_STATE_HIDING && !change && !vis) {
@@ -264,7 +264,7 @@
     }
 
     boolean checkHiddenLw() {
-        if (mWin != null && mWin.isDrawnLw()) {
+        if (mWin != null && mWin.isDrawn()) {
             if (!mWin.isVisibleLw() && !mWin.isAnimatingLw()) {
                 updateStateLw(StatusBarManager.WINDOW_STATE_HIDDEN);
             }
@@ -291,7 +291,7 @@
         } else if (mWin == null) {
             if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar doesn't exist");
             return false;
-        } else if (mWin.isDisplayedLw()) {
+        } else if (mWin.isDisplayed()) {
             if (DEBUG) Slog.d(mTag, "Not showing transient bar, bar already visible");
             return false;
         } else {
diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java
index 167afab..7e55f0a 100644
--- a/services/core/java/com/android/server/wm/CompatModePackages.java
+++ b/services/core/java/com/android/server/wm/CompatModePackages.java
@@ -16,8 +16,8 @@
 
 package com.android.server.wm;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -37,6 +37,7 @@
 import android.util.SparseArray;
 import android.util.Xml;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.FastXmlSerializer;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -333,8 +334,8 @@
                 }
                 try {
                     if (app.hasThread()) {
-                        if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc "
-                                + app.mName + " new compat " + ci);
+                        ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s "
+                                + "new compat %s", app.mName, ci);
                         app.getThread().updatePackageCompatibilityInfo(packageName, ci);
                     }
                 } catch (Exception e) {
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index 9a397fe..01c007e 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -18,6 +18,9 @@
 
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 
+
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
+
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -25,6 +28,8 @@
 import android.window.IDisplayAreaOrganizer;
 import android.window.IDisplayAreaOrganizerController;
 
+import com.android.internal.protolog.common.ProtoLog;
+
 import java.util.HashMap;
 
 public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerController.Stub {
@@ -67,9 +72,12 @@
     @Override
     public void registerOrganizer(IDisplayAreaOrganizer organizer, int feature) {
         enforceStackPermission("registerOrganizer()");
+        final long uid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register display organizer=%s uid=%d",
+                        organizer.asBinder(), uid);
                 if (mOrganizersByFeatureIds.get(feature) != null) {
                     throw new IllegalStateException(
                             "Replacing existing organizer currently unsupported");
@@ -96,9 +104,12 @@
     @Override
     public void unregisterOrganizer(IDisplayAreaOrganizer organizer) {
         enforceStackPermission("unregisterTaskOrganizer()");
+        final long uid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Unregister display organizer=%s uid=%d",
+                        organizer.asBinder(), uid);
                 mOrganizersByFeatureIds.entrySet().removeIf(
                         entry -> entry.getValue().asBinder() == organizer.asBinder());
 
@@ -113,6 +124,7 @@
     }
 
     void onDisplayAreaAppeared(IDisplayAreaOrganizer organizer, DisplayArea da) {
+        ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea appeared name=%s", da.getName());
         try {
             SurfaceControl outSurfaceControl = new SurfaceControl(da.getSurfaceControl(),
                     "DisplayAreaOrganizerController.onDisplayAreaAppeared");
@@ -123,6 +135,7 @@
     }
 
     void onDisplayAreaVanished(IDisplayAreaOrganizer organizer, DisplayArea da) {
+        ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea vanished name=%s", da.getName());
         try {
             organizer.onDisplayAreaVanished(da.getDisplayAreaInfo());
         } catch (RemoteException e) {
@@ -131,6 +144,7 @@
     }
 
     void onDisplayAreaInfoChanged(IDisplayAreaOrganizer organizer, DisplayArea da) {
+        ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea info changed name=%s", da.getName());
         try {
             organizer.onDisplayAreaInfoChanged(da.getDisplayAreaInfo());
         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0215ead..2f7cc69 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -694,7 +694,7 @@
         // Don't do layout of a window if it is not visible, or soon won't be visible, to avoid
         // wasting time and funky changes while a window is animating away.
         final boolean gone = (mTmpWindow != null && mWmService.mPolicy.canBeHiddenByKeyguardLw(w))
-                || w.isGoneForLayoutLw();
+                || w.isGoneForLayout();
 
         if (DEBUG_LAYOUT && !w.mLayoutAttached) {
             Slog.v(TAG, "1ST PASS " + w + ": gone=" + gone + " mHaveFrame=" + w.mHaveFrame
@@ -742,7 +742,7 @@
             if (firstLayout) {
                 // The client may compute its actual requested size according to the first layout,
                 // so we still request the window to resize if the current frame is empty.
-                if (!w.getFrameLw().isEmpty()) {
+                if (!w.getFrame().isEmpty()) {
                     w.updateLastFrames();
                 }
                 w.updateLastInsetValues();
@@ -753,9 +753,9 @@
                 w.mActivityRecord.layoutLetterbox(w);
             }
 
-            if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + w.getFrameLw()
+            if (DEBUG_LAYOUT) Slog.v(TAG, "  LAYOUT: mFrame=" + w.getFrame()
                     + " mContainingFrame=" + w.getContainingFrame()
-                    + " mDisplayFrame=" + w.getDisplayFrameLw());
+                    + " mDisplayFrame=" + w.getDisplayFrame());
         }
     };
 
@@ -780,9 +780,9 @@
                 w.prelayout();
                 getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
                 w.mLayoutSeq = mLayoutSeq;
-                if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrameLw()
+                if (DEBUG_LAYOUT) Slog.v(TAG, " LAYOUT: mFrame=" + w.getFrame()
                         + " mContainingFrame=" + w.getContainingFrame()
-                        + " mDisplayFrame=" + w.getDisplayFrameLw());
+                        + " mDisplayFrame=" + w.getDisplayFrame());
             }
         }
     };
@@ -807,7 +807,7 @@
         w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;
 
         if (!mTmpApplySurfaceChangesTransactionState.obscured) {
-            final boolean isDisplayed = w.isDisplayedLw();
+            final boolean isDisplayed = w.isDisplayed();
 
             if (isDisplayed && w.isObscuringDisplay()) {
                 // This window completely covers everything behind it, so we want to leave all
@@ -2549,7 +2549,7 @@
                 return;
             }
 
-            if (w.isOnScreen() && w.isVisibleLw() && w.getFrameLw().contains(x, y)) {
+            if (w.isOnScreen() && w.isVisibleLw() && w.getFrame().contains(x, y)) {
                 targetWindowType[0] = w.mAttrs.type;
                 return;
             }
@@ -2747,7 +2747,7 @@
     void adjustForImeIfNeeded() {
         final WindowState imeWin = mInputMethodWindow;
         final boolean imeVisible = imeWin != null && imeWin.isVisibleLw()
-                && imeWin.isDisplayedLw();
+                && imeWin.isDisplayed();
         final int imeHeight = mDisplayFrames.getInputMethodWindowVisibleHeight();
         mPinnedStackControllerLocked.setAdjustedForIme(imeVisible, imeHeight);
     }
@@ -3364,7 +3364,7 @@
         // Now, a special case -- if the last target's window is in the process of exiting, but
         // not removed, keep on the last target to avoid IME flicker. The exception is if the
         // current target is home since we want opening apps to become the IME target right away.
-        if (curTarget != null && !curTarget.mRemoved && curTarget.isDisplayedLw()
+        if (curTarget != null && !curTarget.mRemoved && curTarget.isDisplayed()
                 && curTarget.isClosing() && !curTarget.isActivityTypeHome()) {
             if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, "Not changing target till current window is"
                     + " closing and not removed");
@@ -3647,7 +3647,7 @@
 
         final WindowState visibleNotDrawnWindow = getWindow(w -> {
             final boolean isVisible = w.isVisible() && !w.mObscured;
-            final boolean isDrawn = w.isDrawnLw();
+            final boolean isDrawn = w.isDrawn();
             if (isVisible && !isDrawn) {
                 ProtoLog.d(WM_DEBUG_BOOT,
                         "DisplayContent: boot is waiting for window of type %d to be drawn",
@@ -4599,9 +4599,9 @@
             DisplayContent dc = this;
             do {
                 final WindowState displayParent = dc.getParentWindow();
-                location.x += displayParent.getFrameLw().left
+                location.x += displayParent.getFrame().left
                         + (dc.getLocationInParentWindow().x * displayParent.mGlobalScale + 0.5f);
-                location.y += displayParent.getFrameLw().top
+                location.y += displayParent.getFrame().top
                         + (dc.getLocationInParentWindow().y * displayParent.mGlobalScale + 0.5f);
                 dc = displayParent.getDisplayContent();
             } while (dc != null && dc.getParentWindow() != null);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 40fc25b..1c147c2 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -195,13 +195,13 @@
 import com.android.server.LocalServices;
 import com.android.server.UiThread;
 import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.policy.WindowManagerPolicy.InputConsumer;
 import com.android.server.policy.WindowManagerPolicy.NavigationBarPosition;
 import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
 import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
 import com.android.server.policy.WindowOrientationListener;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wallpaper.WallpaperManagerInternal;
+import com.android.server.wm.InputMonitor.EventReceiverInputConsumer;
 import com.android.server.wm.utils.InsetUtils;
 
 import java.io.PrintWriter;
@@ -416,7 +416,7 @@
     private boolean mAllowLockscreenWhenOn;
 
     @VisibleForTesting
-    InputConsumer mInputConsumer = null;
+    EventReceiverInputConsumer mInputConsumer;
 
     private PointerLocationView mPointerLocationView;
 
@@ -462,7 +462,7 @@
                     }
                     break;
                 case MSG_DISPOSE_INPUT_CONSUMER:
-                    disposeInputConsumer((InputConsumer) msg.obj);
+                    disposeInputConsumer((EventReceiverInputConsumer) msg.obj);
                     break;
                 case MSG_ENABLE_POINTER_LOCATION:
                     enablePointerLocation();
@@ -1126,7 +1126,7 @@
 
                         // For IME we use regular frame.
                         (displayFrames, windowState, inOutFrame) ->
-                                inOutFrame.set(windowState.getFrameLw()));
+                                inOutFrame.set(windowState.getFrame()));
 
                 mDisplayContent.setInsetProvider(ITYPE_BOTTOM_GESTURES, win,
                         (displayFrames, windowState, inOutFrame) -> {
@@ -1203,11 +1203,11 @@
                 // IME should not provide frame which is smaller than the nav bar frame. Otherwise,
                 // nav bar might be overlapped with the content of the client when IME is shown.
                 sTmpRect.set(inOutFrame);
-                sTmpRect.intersectUnchecked(mNavigationBar.getFrameLw());
-                inOutFrame.inset(windowState.getGivenContentInsetsLw());
+                sTmpRect.intersectUnchecked(mNavigationBar.getFrame());
+                inOutFrame.inset(windowState.mGivenContentInsets);
                 inOutFrame.union(sTmpRect);
             } else {
-                inOutFrame.inset(windowState.getGivenContentInsetsLw());
+                inOutFrame.inset(windowState.mGivenContentInsets);
             }
         };
     }
@@ -2075,7 +2075,7 @@
 
             // In case we forced the window to draw behind the navigation bar, restrict df to
             // DF.Restricted to simulate old compat behavior.
-            Rect parentDisplayFrame = attached.getDisplayFrameLw();
+            Rect parentDisplayFrame = attached.getDisplayFrame();
             final WindowManager.LayoutParams attachedAttrs = attached.mAttrs;
             if ((attachedAttrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS) != 0
                     && (attachedAttrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
@@ -2096,14 +2096,14 @@
                 // setting {@link WindowManager.LayoutParams#FLAG_LAYOUT_ATTACHED_IN_DECOR} flag.
                 // Otherwise, use the overscan frame.
                 cf.set((fl & FLAG_LAYOUT_ATTACHED_IN_DECOR) != 0
-                        ? attached.getContentFrameLw() : parentDisplayFrame);
+                        ? attached.getContentFrame() : parentDisplayFrame);
             } else {
                 // If the window is resizing, then we want to base the content frame on our attached
                 // content frame to resize...however, things can be tricky if the attached window is
                 // NOT in resize mode, in which case its content frame will be larger.
                 // Ungh. So to deal with that, make sure the content frame we end up using is not
                 // covering the IM dock.
-                cf.set(attached.getContentFrameLw());
+                cf.set(attached.getContentFrame());
                 if (attached.isVoiceInteraction()) {
                     cf.intersectUnchecked(displayFrames.mVoiceContent);
                 } else if (win.isInputMethodTarget() || attached.isInputMethodTarget()) {
@@ -2111,11 +2111,11 @@
                 }
             }
             df.set(insetDecors ? parentDisplayFrame : cf);
-            vf.set(attached.getVisibleFrameLw());
+            vf.set(attached.getVisibleFrame());
         }
         // The LAYOUT_IN_SCREEN flag is used to determine whether the attached window should be
         // positioned relative to its parent or the entire screen.
-        pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df);
+        pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df);
     }
 
     private void applyStableConstraints(int sysui, int fl, Rect r, DisplayFrames displayFrames) {
@@ -2217,8 +2217,8 @@
                 vf.set(adjust != SOFT_INPUT_ADJUST_NOTHING
                         ? displayFrames.mCurrent : displayFrames.mDock);
             } else {
-                pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrameLw() : df);
-                vf.set(attached.getVisibleFrameLw());
+                pf.set((fl & FLAG_LAYOUT_IN_SCREEN) == 0 ? attached.getFrame() : df);
+                vf.set(attached.getVisibleFrame());
             }
             cf.set(adjust != SOFT_INPUT_ADJUST_RESIZE
                     ? displayFrames.mDock : displayFrames.mContent);
@@ -2623,12 +2623,10 @@
         win.computeFrame(displayFrames);
         // Dock windows carve out the bottom of the screen, so normal windows
         // can't appear underneath them.
-        if (type == TYPE_INPUT_METHOD && win.isVisibleLw()
-                && !win.getGivenInsetsPendingLw()) {
+        if (type == TYPE_INPUT_METHOD && win.isVisibleLw() && !win.mGivenInsetsPending) {
             offsetInputMethodWindowLw(win, displayFrames);
         }
-        if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw()
-                && !win.getGivenInsetsPendingLw()) {
+        if (type == TYPE_VOICE_INTERACTION && win.isVisibleLw() && !win.mGivenInsetsPending) {
             offsetVoiceInputWindowLw(win, displayFrames);
         }
     }
@@ -2645,8 +2643,8 @@
         final int navBarPosition = navigationBarPosition(displayFrames.mDisplayWidth,
                 displayFrames.mDisplayHeight, rotation);
 
-        int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
-        top += win.getGivenContentInsetsLw().top;
+        int top = Math.max(win.getDisplayFrame().top, win.getContentFrame().top);
+        top += win.mGivenContentInsets.top;
         displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top);
         if (navBarPosition == NAV_BAR_BOTTOM) {
             // Always account for the nav bar frame height on the bottom since in all navigation
@@ -2658,8 +2656,8 @@
                     displayFrames.mUnrestricted.bottom - navFrameHeight);
         }
         displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
-        top = win.getVisibleFrameLw().top;
-        top += win.getGivenVisibleInsetsLw().top;
+        top = win.getVisibleFrame().top;
+        top += win.mGivenVisibleInsets.top;
         displayFrames.mCurrent.bottom = Math.min(displayFrames.mCurrent.bottom, top);
         if (DEBUG_LAYOUT) Slog.v(TAG, "Input method: mDockBottom="
                 + displayFrames.mDock.bottom + " mContentBottom="
@@ -2667,8 +2665,8 @@
     }
 
     private void offsetVoiceInputWindowLw(WindowState win, DisplayFrames displayFrames) {
-        int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
-        top += win.getGivenContentInsetsLw().top;
+        int top = Math.max(win.getDisplayFrame().top, win.getContentFrame().top);
+        top += win.mGivenContentInsets.top;
         displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
     }
 
@@ -2729,8 +2727,7 @@
             if (win.isDreamWindow()) {
                 // If the lockscreen was showing when the dream started then wait
                 // for the dream to draw before hiding the lockscreen.
-                if (!mDreamingLockscreen
-                        || (win.isVisibleLw() && win.hasDrawnLw())) {
+                if (!mDreamingLockscreen || (win.isVisibleLw() && win.hasDrawn())) {
                     mShowingDream = true;
                     appWindow = true;
                 }
@@ -2916,7 +2913,7 @@
         final InsetsSource request = mTopFullscreenOpaqueWindowState.getRequestedInsetsState()
                 .peekSource(ITYPE_STATUS_BAR);
         if (WindowManagerDebugConfig.DEBUG) {
-            Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw());
+            Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrame());
             Slog.d(TAG, "attr: " + attrs + " request: " + request);
         }
         return (fl & LayoutParams.FLAG_FULLSCREEN) != 0
@@ -3398,7 +3395,7 @@
         mImmersiveModeConfirmation.confirmCurrentPrompt();
     }
 
-    private void disposeInputConsumer(InputConsumer inputConsumer) {
+    private void disposeInputConsumer(EventReceiverInputConsumer inputConsumer) {
         if (inputConsumer != null) {
             inputConsumer.dispose();
         }
@@ -4171,6 +4168,6 @@
             return false;
         }
 
-        return Rect.intersects(targetWindow.getFrameLw(), navBarWindow.getFrameLw());
+        return Rect.intersects(targetWindow.getFrame(), navBarWindow.getFrame());
     }
 }
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 133b111..c9f463b 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -283,8 +283,9 @@
             mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
             mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
             mDragWindowHandle.visible = true;
-            mDragWindowHandle.canReceiveKeys = false;
-            mDragWindowHandle.hasFocus = true;
+            // Allows the system to consume keys when dragging is active. This can also be used to
+            // modify the drag state on key press. Example, cancel drag on escape key.
+            mDragWindowHandle.focusable = true;
             mDragWindowHandle.hasWallpaper = false;
             mDragWindowHandle.paused = false;
             mDragWindowHandle.ownerPid = Process.myPid();
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 86e2698..f0f3385 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -93,7 +93,7 @@
                 || (mImeTargetFromIme != null
                 && isImeTargetFromDisplayContentAndImeSame()
                 && mWin != null
-                && mWin.isDrawnLw()
+                && mWin.isDrawn()
                 && !mWin.mGivenInsetsPending)) {
             mIsImeLayoutDrawn = true;
             // show IME if InputMethodService requested it to be shown.
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 3b24584b..a79d3bb 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -80,8 +80,7 @@
         mWindowHandle.layoutParamsFlags = 0;
         mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
         mWindowHandle.visible = true;
-        mWindowHandle.canReceiveKeys = false;
-        mWindowHandle.hasFocus = false;
+        mWindowHandle.focusable = false;
         mWindowHandle.hasWallpaper = false;
         mWindowHandle.paused = false;
         mWindowHandle.ownerPid = Process.myPid();
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index cec797c..fb511e0 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -61,7 +61,6 @@
 import android.view.SurfaceControl;
 
 import com.android.internal.protolog.common.ProtoLog;
-import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
 import java.util.Set;
@@ -95,8 +94,11 @@
      */
     private final ArrayMap<String, InputConsumerImpl> mInputConsumers = new ArrayMap();
 
-    private static final class EventReceiverInputConsumer extends InputConsumerImpl
-            implements WindowManagerPolicy.InputConsumer {
+    /**
+     * Representation of a input consumer that the policy has added to the window manager to consume
+     * input events going to windows below it.
+     */
+    static final class EventReceiverInputConsumer extends InputConsumerImpl {
         private InputMonitor mInputMonitor;
         private final InputEventReceiver mInputEventReceiver;
 
@@ -111,8 +113,8 @@
                     mClientChannel, looper);
         }
 
-        @Override
-        public void dismiss() {
+        /** Removes the input consumer from the window manager. */
+        void dismiss() {
             synchronized (mService.mGlobalLock) {
                 mInputMonitor.mInputConsumers.remove(mName);
                 hide(mInputMonitor.mInputTransaction);
@@ -120,8 +122,8 @@
             }
         }
 
-        @Override
-        public void dispose() {
+        /** Disposes the input consumer and input receiver from the associated thread. */
+        void dispose() {
             synchronized (mService.mGlobalLock) {
                 disposeChannelsLw(mInputMonitor.mInputTransaction);
                 mInputEventReceiver.dispose();
@@ -225,7 +227,7 @@
         }
     }
 
-    WindowManagerPolicy.InputConsumer createInputConsumer(Looper looper, String name,
+    EventReceiverInputConsumer createInputConsumer(Looper looper, String name,
             InputEventReceiver.Factory inputEventReceiverFactory) {
         if (!name.contentEquals(INPUT_CONSUMER_NAVIGATION)) {
             throw new IllegalArgumentException("Illegal input consumer : " + name
@@ -281,8 +283,7 @@
         inputWindowHandle.layoutParamsType = type;
         inputWindowHandle.dispatchingTimeoutMillis = child.getInputDispatchingTimeoutMillis();
         inputWindowHandle.visible = isVisible;
-        inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
-        inputWindowHandle.hasFocus = hasFocus;
+        inputWindowHandle.focusable = hasFocus;
         inputWindowHandle.hasWallpaper = hasWallpaper;
         inputWindowHandle.paused = child.mActivityRecord != null ? child.mActivityRecord.paused : false;
         inputWindowHandle.ownerPid = child.mSession.mPid;
@@ -290,7 +291,7 @@
         inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
         inputWindowHandle.displayId = child.getDisplayId();
 
-        final Rect frame = child.getFrameLw();
+        final Rect frame = child.getFrame();
         inputWindowHandle.frameLeft = frame.left;
         inputWindowHandle.frameTop = frame.top;
         inputWindowHandle.frameRight = frame.right;
@@ -579,8 +580,7 @@
         inputWindowHandle.layoutParamsType = type;
         inputWindowHandle.dispatchingTimeoutMillis = 0; // it should never receive input
         inputWindowHandle.visible = isVisible;
-        inputWindowHandle.canReceiveKeys = false;
-        inputWindowHandle.hasFocus = false;
+        inputWindowHandle.focusable = false;
         inputWindowHandle.inputFeatures = INPUT_FEATURE_NO_INPUT_CHANNEL;
         inputWindowHandle.scaleFactor = 1;
         inputWindowHandle.layoutParamsFlags =
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index d1eb795..e00c9e7 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -173,7 +173,7 @@
         // frame may not yet determined that server side doesn't think the window is ready to
         // visible. (i.e. No surface, pending insets that were given during layout, etc..)
         if (mServerVisible) {
-            mTmpRect.set(mWin.getFrameLw());
+            mTmpRect.set(mWin.getFrame());
             if (mFrameProvider != null) {
                 mFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin, mTmpRect);
             } else {
@@ -185,14 +185,14 @@
         mSource.setFrame(mTmpRect);
 
         if (mImeFrameProvider != null) {
-            mImeOverrideFrame.set(mWin.getFrameLw());
+            mImeOverrideFrame.set(mWin.getFrame());
             mImeFrameProvider.accept(mWin.getDisplayContent().mDisplayFrames, mWin,
                     mImeOverrideFrame);
         }
 
         if (mWin.mGivenVisibleInsets.left != 0 || mWin.mGivenVisibleInsets.top != 0
                 || mWin.mGivenVisibleInsets.right != 0 || mWin.mGivenVisibleInsets.bottom != 0) {
-            mTmpRect.set(mWin.getFrameLw());
+            mTmpRect.set(mWin.getFrame());
             mTmpRect.inset(mWin.mGivenVisibleInsets);
             mSource.setVisibleFrame(mTmpRect);
         } else {
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 8ef57f7..c8d7693 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -29,7 +29,7 @@
 import static android.os.UserHandle.USER_CURRENT;
 import static android.telecom.TelecomManager.EMERGENCY_DIALER_COMPONENT;
 
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_LOCKTASK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -65,6 +65,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.LocalServices;
@@ -448,7 +449,7 @@
      * unlike {@link #stopLockTaskMode(Task, boolean, int)}, it doesn't perform the checks.
      */
     void clearLockedTasks(String reason) {
-        if (DEBUG_LOCKTASK) Slog.i(TAG_LOCKTASK, "clearLockedTasks: " + reason);
+        ProtoLog.i(WM_DEBUG_LOCKTASK, "clearLockedTasks: %s", reason);
         if (!mLockTaskModeTasks.isEmpty()) {
             clearLockedTask(mLockTaskModeTasks.get(0));
         }
@@ -490,10 +491,10 @@
         if (!mLockTaskModeTasks.remove(task)) {
             return;
         }
-        if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: removed " + task);
+        ProtoLog.d(WM_DEBUG_LOCKTASK, "removeLockedTask: removed %s", task);
         if (mLockTaskModeTasks.isEmpty()) {
-            if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "removeLockedTask: task=" + task +
-                    " last task, reverting locktask mode. Callers=" + Debug.getCallers(3));
+            ProtoLog.d(WM_DEBUG_LOCKTASK, "removeLockedTask: task=%s last task, "
+                    + "reverting locktask mode. Callers=%s", task, Debug.getCallers(3));
             mHandler.post(() -> performStopLockTask(task.mUserId));
         }
     }
@@ -558,7 +559,7 @@
             if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
                 // startLockTask() called by app, but app is not part of lock task allowlist. Show
                 // app pinning request. We will come back here with isSystemCaller true.
-                if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "Mode default, asking user");
+                ProtoLog.w(WM_DEBUG_LOCKTASK, "Mode default, asking user");
                 StatusBarManagerInternal statusBarManager = LocalServices.getService(
                         StatusBarManagerInternal.class);
                 if (statusBarManager != null) {
@@ -569,8 +570,7 @@
         }
 
         // System can only initiate screen pinning, not full lock task mode
-        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
-                isSystemCaller ? "Locking pinned" : "Locking fully");
+        ProtoLog.w(WM_DEBUG_LOCKTASK, "%s", isSystemCaller ? "Locking pinned" : "Locking fully");
         setLockTaskMode(task, isSystemCaller ? LOCK_TASK_MODE_PINNED : LOCK_TASK_MODE_LOCKED,
                 "startLockTask", true);
     }
@@ -584,7 +584,7 @@
                                  String reason, boolean andResume) {
         // Should have already been checked, but do it again.
         if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
-            if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
+            ProtoLog.w(WM_DEBUG_LOCKTASK,
                     "setLockTaskMode: Can't lock due to auth");
             return;
         }
@@ -602,8 +602,8 @@
                     task.mUserId,
                     lockTaskModeState));
         }
-        if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK, "setLockTaskMode: Locking to " + task +
-                " Callers=" + Debug.getCallers(4));
+        ProtoLog.w(WM_DEBUG_LOCKTASK, "setLockTaskMode: Locking to %s Callers=%s",
+                task, Debug.getCallers(4));
 
         if (!mLockTaskModeTasks.contains(task)) {
             mLockTaskModeTasks.add(task);
@@ -672,8 +672,8 @@
             }
 
             // Terminate locked tasks that have recently lost allowlist authorization.
-            if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "onLockTaskPackagesUpdated: removing " +
-                    lockedTask + " mLockTaskAuth()=" + lockedTask.lockTaskAuthToString());
+            ProtoLog.d(WM_DEBUG_LOCKTASK, "onLockTaskPackagesUpdated: removing %s"
+                    + " mLockTaskAuth()=%s", lockedTask, lockedTask.lockTaskAuthToString());
             removeLockedTask(lockedTask);
             lockedTask.performClearTaskLocked();
             taskChanged = true;
@@ -686,8 +686,8 @@
         if (mLockTaskModeTasks.isEmpty() && task!= null
                 && task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE) {
             // This task must have just been authorized.
-            if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK,
-                    "onLockTaskPackagesUpdated: starting new locktask task=" + task);
+            ProtoLog.d(WM_DEBUG_LOCKTASK, "onLockTaskPackagesUpdated: starting new "
+                    + "locktask task=%s", task);
             setLockTaskMode(task, LOCK_TASK_MODE_LOCKED, "package updated", false);
             taskChanged = true;
         }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 143fbb0..6882dc4 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -821,7 +821,7 @@
                 : null;
         if (targetAppMainWindow != null) {
             targetAppMainWindow.getBounds(mTmpRect);
-            inputWindowHandle.hasFocus = hasFocus;
+            inputWindowHandle.focusable = hasFocus;
             inputWindowHandle.touchableRegion.set(mTmpRect);
             return true;
         }
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index cc5ed36..c3953b4 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -16,9 +16,8 @@
 
 package com.android.server.wm;
 
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
-import static com.android.server.wm.Task.TAG_ADD_REMOVE;
 import static com.android.server.wm.Task.TAG_TASKS;
 
 import android.app.ActivityOptions;
@@ -27,6 +26,7 @@
 import android.os.Debug;
 import android.util.Slog;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledFunction;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -218,8 +218,8 @@
             if (takeOptions) {
                 noOptions = takeOption(p, noOptions);
             }
-            if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE, "Removing activity " + p + " from task="
-                    + mTask + " adding to task=" + targetTask + " Callers=" + Debug.getCallers(4));
+            ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing activity %s from task=%s "
+                    + "adding to task=%s Callers=%s", p, mTask,  targetTask, Debug.getCallers(4));
             if (DEBUG_TASKS) Slog.v(TAG_TASKS,
                     "Pushing next activity " + p + " out to target's task " + target);
             p.reparent(targetTask, position, "resetTargetTaskIfNeeded");
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index aeaffd9..958adaa 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1106,14 +1106,14 @@
         final WindowManager.LayoutParams attrs = w.mAttrs;
         final int attrFlags = attrs.flags;
         final boolean onScreen = w.isOnScreen();
-        final boolean canBeSeen = w.isDisplayedLw();
+        final boolean canBeSeen = w.isDisplayed();
         final int privateflags = attrs.privateFlags;
         boolean displayHasContent = false;
 
         ProtoLog.d(WM_DEBUG_KEEP_SCREEN_ON,
                 "handleNotObscuredLocked w: %s, w.mHasSurface: %b, w.isOnScreen(): %b, w"
                         + ".isDisplayedLw(): %b, w.mAttrs.userActivityTimeout: %d",
-                w, w.mHasSurface, onScreen, w.isDisplayedLw(), w.mAttrs.userActivityTimeout);
+                w, w.mHasSurface, onScreen, w.isDisplayed(), w.mAttrs.userActivityTimeout);
         if (w.mHasSurface && onScreen) {
             if (!syswin && w.mAttrs.userActivityTimeout >= 0 && mUserActivityTimeout < 0) {
                 mUserActivityTimeout = w.mAttrs.userActivityTimeout;
@@ -2289,10 +2289,6 @@
 
         for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
             final DisplayContent display = getChildAt(displayNdx);
-            if (display.shouldSleep()) {
-                continue;
-            }
-
             final boolean curResult = result;
             boolean resumedOnDisplay = display.reduceOnAllTaskDisplayAreas(
                     (taskDisplayArea, resumed) -> {
@@ -2716,7 +2712,7 @@
                 + " resumed=" + r.getStack().mResumedActivity + " pausing="
                 + r.getStack().mPausingActivity + " for reason " + mDestroyAllActivitiesReason);
 
-        r.destroyImmediately(true /* removeFromTask */, mDestroyAllActivitiesReason);
+        r.destroyImmediately(mDestroyAllActivitiesReason);
     }
 
     // Tries to put all activity stacks to sleep. Returns true if all stacks were
@@ -3124,7 +3120,6 @@
     FinishDisabledPackageActivitiesHelper mFinishDisabledPackageActivitiesHelper =
             new FinishDisabledPackageActivitiesHelper();
     class FinishDisabledPackageActivitiesHelper {
-        private boolean mDidSomething;
         private String mPackageName;
         private Set<String> mFilterByClasses;
         private boolean mDoit;
@@ -3132,11 +3127,10 @@
         private int mUserId;
         private boolean mOnlyRemoveNoProcess;
         private Task mLastTask;
-        private ComponentName mHomeActivity;
+        private final ArrayList<ActivityRecord> mCollectedActivities = new ArrayList<>();
 
         private void reset(String packageName, Set<String> filterByClasses,
                 boolean doit, boolean evenPersistent, int userId, boolean onlyRemoveNoProcess) {
-            mDidSomething = false;
             mPackageName = packageName;
             mFilterByClasses = filterByClasses;
             mDoit = doit;
@@ -3144,7 +3138,6 @@
             mUserId = userId;
             mOnlyRemoveNoProcess = onlyRemoveNoProcess;
             mLastTask = null;
-            mHomeActivity = null;
         }
 
         boolean process(String packageName, Set<String> filterByClasses,
@@ -3152,14 +3145,35 @@
             reset(packageName, filterByClasses, doit, evenPersistent, userId, onlyRemoveNoProcess);
 
             final PooledFunction f = PooledLambda.obtainFunction(
-                    FinishDisabledPackageActivitiesHelper::processActivity, this,
+                    FinishDisabledPackageActivitiesHelper::collectActivity, this,
                     PooledLambda.__(ActivityRecord.class));
             forAllActivities(f);
             f.recycle();
-            return mDidSomething;
+
+            boolean didSomething = false;
+            final int size = mCollectedActivities.size();
+            // Keep the finishing order from top to bottom.
+            for (int i = 0; i < size; i++) {
+                final ActivityRecord r = mCollectedActivities.get(i);
+                if (mOnlyRemoveNoProcess) {
+                    if (!r.hasProcess()) {
+                        didSomething = true;
+                        Slog.i(TAG, "  Force removing " + r);
+                        r.cleanUp(false /* cleanServices */, false /* setState */);
+                        r.removeFromHistory("force-stop");
+                    }
+                } else {
+                    didSomething = true;
+                    Slog.i(TAG, "  Force finishing " + r);
+                    r.finishIfPossible("force-stop", true /* oomAdj */);
+                }
+            }
+            mCollectedActivities.clear();
+
+            return didSomething;
         }
 
-        private boolean processActivity(ActivityRecord r) {
+        private boolean collectActivity(ActivityRecord r) {
             final boolean sameComponent =
                     (r.packageName.equals(mPackageName) && (mFilterByClasses == null
                             || mFilterByClasses.contains(r.mActivityComponent.getClassName())))
@@ -3176,26 +3190,7 @@
                     }
                     return true;
                 }
-                if (r.isActivityTypeHome()) {
-                    if (mHomeActivity != null && mHomeActivity.equals(r.mActivityComponent)) {
-                        Slog.i(TAG, "Skip force-stop again " + r);
-                        return false;
-                    } else {
-                        mHomeActivity = r.mActivityComponent;
-                    }
-                }
-                if (mOnlyRemoveNoProcess) {
-                    if (noProcess) {
-                        mDidSomething = true;
-                        Slog.i(TAG, "  Force removing " + r);
-                        r.cleanUp(false /* cleanServices */, false /* setState */);
-                        r.removeFromHistory("force-stop");
-                    }
-                } else {
-                    mDidSomething = true;
-                    Slog.i(TAG, "  Force finishing " + r);
-                    r.finishIfPossible("force-stop", true /* oomAdj */);
-                }
+                mCollectedActivities.add(r);
                 mLastTask = r.getTask();
             }
 
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 6cf9432..7b5b0ad 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -99,9 +99,8 @@
                 // the task's profile
                 return;
             }
-            if (!mAllowed && !task.isActivityTypeHome()) {
-                // Skip if the caller isn't allowed to fetch this task, except for the home
-                // task which we always return.
+            if (!mAllowed) {
+                // Skip if the caller isn't allowed to fetch this task
                 return;
             }
         }
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 3c8036d..25732e7 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -101,7 +101,6 @@
     private final Transformation mRotateExitTransformation = new Transformation();
     private final Transformation mRotateEnterTransformation = new Transformation();
     // Complete transformations being applied.
-    private final Transformation mEnterTransformation = new Transformation();
     private final Matrix mSnapshotInitialMatrix = new Matrix();
     private final WindowManagerService mService;
     /** Only used for custom animations and not screen rotation. */
@@ -309,8 +308,6 @@
         pw.print(" "); mRotateExitTransformation.printShortString(pw); pw.println();
         pw.print(prefix); pw.print("mRotateEnterAnimation="); pw.print(mRotateEnterAnimation);
         pw.print(" "); mRotateEnterTransformation.printShortString(pw); pw.println();
-        pw.print(prefix); pw.print("mEnterTransformation=");
-        mEnterTransformation.printShortString(pw); pw.println();
         pw.print(prefix); pw.print("mSnapshotInitialMatrix=");
         mSnapshotInitialMatrix.dump(pw); pw.println();
         pw.print(prefix); pw.print("mForceDefaultOrientation="); pw.print(mForceDefaultOrientation);
@@ -508,10 +505,6 @@
         return mCurRotation != mOriginalRotation;
     }
 
-    public Transformation getEnterTransformation() {
-        return mEnterTransformation;
-    }
-
     /**
      * Utility class that runs a {@link ScreenRotationAnimation} on the {@link
      * SurfaceAnimationRunner}.
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimator.java b/services/core/java/com/android/server/wm/SurfaceAnimator.java
index 33935d6..7df2b40 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimator.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimator.java
@@ -26,6 +26,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.util.DebugUtils;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.SurfaceControl;
@@ -429,7 +430,8 @@
 
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mLeash="); pw.print(mLeash);
-        pw.print(" mAnimationType=" + mAnimationType);
+        pw.print(" mAnimationType=" + DebugUtils.valueToString(SurfaceAnimator.class,
+                "ANIMATION_TYPE_", mAnimationType));
         pw.println(mAnimationStartDelayed ? " mAnimationStartDelayed=true" : "");
         pw.print(prefix); pw.print("Animation: "); pw.println(mAnimation);
         if (mAnimation != null) {
@@ -442,56 +444,56 @@
      * No animation is specified.
      * @hide
      */
-    static final int ANIMATION_TYPE_NONE = 0;
+    public static final int ANIMATION_TYPE_NONE = 0;
 
     /**
      * Animation for an app transition.
      * @hide
      */
-    static final int ANIMATION_TYPE_APP_TRANSITION = 1;
+    public static final int ANIMATION_TYPE_APP_TRANSITION = 1;
 
     /**
      * Animation for screen rotation.
      * @hide
      */
-    static final int ANIMATION_TYPE_SCREEN_ROTATION = 1 << 1;
+    public static final int ANIMATION_TYPE_SCREEN_ROTATION = 1 << 1;
 
     /**
      * Animation for dimming.
      * @hide
      */
-    static final int ANIMATION_TYPE_DIMMER = 1 << 2;
+    public static final int ANIMATION_TYPE_DIMMER = 1 << 2;
 
     /**
      * Animation for recent apps.
      * @hide
      */
-    static final int ANIMATION_TYPE_RECENTS = 1 << 3;
+    public static final int ANIMATION_TYPE_RECENTS = 1 << 3;
 
     /**
      * Animation for a {@link WindowState} without animating the activity.
      * @hide
      */
-    static final int ANIMATION_TYPE_WINDOW_ANIMATION = 1 << 4;
+    public static final int ANIMATION_TYPE_WINDOW_ANIMATION = 1 << 4;
 
     /**
      * Animation to control insets. This is actually not an animation, but is used to give the
      * client a leash over the system window causing insets.
      * @hide
      */
-    static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5;
+    public static final int ANIMATION_TYPE_INSETS_CONTROL = 1 << 5;
 
     /**
      * Animation when a fixed rotation transform is applied to a window token.
      * @hide
      */
-    static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6;
+    public static final int ANIMATION_TYPE_FIXED_TRANSFORM = 1 << 6;
 
     /**
      * Bitmask to include all animation types. This is NOT an {@link AnimationType}
      * @hide
      */
-    static final int ANIMATION_TYPE_ALL = -1;
+    public static final int ANIMATION_TYPE_ALL = -1;
 
     /**
      * The type of the animation.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 50c269e..bfaaf46 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -79,6 +79,7 @@
 import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
 import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
 import static com.android.server.wm.ActivityRecord.STARTING_WINDOW_SHOWN;
 import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
@@ -87,9 +88,7 @@
 import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
 import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CLEANUP;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_LOCKTASK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PAUSE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS;
@@ -783,7 +782,7 @@
      * taskAppeared callback, and emit a taskRemoved callback when the Task is vanished.
      */
     ITaskOrganizer mTaskOrganizer;
-    private int mLastTaskOrganizerWindowingMode = -1;
+
     /**
      * Prevent duplicate calls to onTaskAppeared.
      */
@@ -798,6 +797,7 @@
      *     organizer for ordering purposes.</li>
      * </ul>
      */
+    @VisibleForTesting
     boolean mCreatedByOrganizer;
 
     /**
@@ -1650,8 +1650,8 @@
      * Reorder the history stack so that the passed activity is brought to the front.
      */
     final void moveActivityToFrontLocked(ActivityRecord newTop) {
-        if (DEBUG_ADD_REMOVE) Slog.i(TAG_ADD_REMOVE, "Removing and adding activity "
-                + newTop + " to stack at top callers=" + Debug.getCallers(4));
+        ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Removing and adding activity %s to stack at top "
+                + "callers=%s", newTop, Debug.getCallers(4));
 
         positionChildAtTop(newTop);
         updateEffectiveIntent();
@@ -1950,8 +1950,8 @@
                         ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
                 break;
         }
-        if (DEBUG_LOCKTASK) Slog.d(TAG_LOCKTASK, "setLockTaskAuth: task=" + this
-                + " mLockTaskAuth=" + lockTaskAuthToString());
+        ProtoLog.d(WM_DEBUG_LOCKTASK, "setLockTaskAuth: task=%s mLockTaskAuth=%s", this,
+                lockTaskAuthToString());
     }
 
     @Override
@@ -2788,8 +2788,16 @@
             // For calculating screen layout, we need to use the non-decor inset screen area for the
             // calculation for compatibility reasons, i.e. screen area without system bars that
             // could never go away in Honeycomb.
-            final int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
-            final int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
+            int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density);
+            int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density);
+            // Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is
+            // undefined so it can't be used.
+            if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) {
+                compatScreenWidthDp = inOutConfig.screenWidthDp;
+            }
+            if (inOutConfig.screenHeightDp != Configuration.SCREEN_HEIGHT_DP_UNDEFINED) {
+                compatScreenHeightDp = inOutConfig.screenHeightDp;
+            }
             // Reducing the screen layout starting from its parent config.
             inOutConfig.screenLayout = computeScreenLayoutOverride(parentConfig.screenLayout,
                     compatScreenWidthDp, compatScreenHeightDp);
@@ -4823,19 +4831,18 @@
             return false;
         }
 
-        ITaskOrganizer previousOrganizer = mTaskOrganizer;
+        ITaskOrganizer prevOrganizer = mTaskOrganizer;
         // Update the new task organizer before calling sendTaskVanished since it could result in
         // a new SurfaceControl getting created that would notify the old organizer about it.
         mTaskOrganizer = organizer;
         // Let the old organizer know it has lost control.
-        sendTaskVanished(previousOrganizer);
+        sendTaskVanished(prevOrganizer);
 
         if (mTaskOrganizer != null) {
             sendTaskAppeared();
         } else {
             // No longer managed by any organizer.
             mTaskAppearedSent = false;
-            mLastTaskOrganizerWindowingMode = -1;
             setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, false /* set */);
             if (mCreatedByOrganizer) {
                 removeImmediately();
@@ -4856,29 +4863,16 @@
      */
     boolean updateTaskOrganizerState(boolean forceUpdate) {
         if (!isRootTask()) {
-            final boolean result = setTaskOrganizer(null);
-            mLastTaskOrganizerWindowingMode = -1;
-            return result;
+            return setTaskOrganizer(null);
         }
 
         final int windowingMode = getWindowingMode();
-        if (!forceUpdate && windowingMode == mLastTaskOrganizerWindowingMode) {
-            // If our windowing mode hasn't actually changed, then just stick
-            // with our old organizer. This lets us implement the semantic
-            // where SysUI can continue to manage it's old tasks
-            // while CTS temporarily takes over the registration.
+        final TaskOrganizerController controller = mWmService.mAtmService.mTaskOrganizerController;
+        final ITaskOrganizer organizer = controller.getTaskOrganizer(windowingMode);
+        if (!forceUpdate && mTaskOrganizer == organizer) {
             return false;
         }
-        /*
-         * Different windowing modes may be managed by different task organizers. If
-         * getTaskOrganizer returns null, we still call setTaskOrganizer to
-         * make sure we clear it.
-         */
-        final ITaskOrganizer org =
-                mWmService.mAtmService.mTaskOrganizerController.getTaskOrganizer(windowingMode);
-        final boolean result = setTaskOrganizer(org);
-        mLastTaskOrganizerWindowingMode = org != null ? windowingMode : -1;
-        return result;
+        return setTaskOrganizer(organizer);
     }
 
     @Override
@@ -6195,10 +6189,6 @@
 
             next.setState(RESUMED, "resumeTopActivityInnerLocked");
 
-            next.app.updateProcessInfo(false /* updateServiceConnectionActivities */,
-                    true /* activityChange */, true /* updateOomAdj */,
-                    true /* addPendingTopUid */);
-
             // Have the window manager re-evaluate the orientation of
             // the screen based on the new activity order.
             boolean notUpdated = true;
@@ -6358,7 +6348,7 @@
         return mRootWindowContainer.resumeHomeActivity(prev, reason, getDisplayArea());
     }
 
-    void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
+    void startActivityLocked(ActivityRecord r, @Nullable ActivityRecord focusedTopActivity,
             boolean newTask, boolean keepCurTransition, ActivityOptions options) {
         Task rTask = r.getTask();
         final boolean allowMoveToFront = options == null || !options.getAvoidMoveToFront();
@@ -6379,7 +6369,8 @@
                 // Here it is!  Now, if this is not yet visible (occluded by another task) to the
                 // user, then just add it without starting; it will get started when the user
                 // navigates back to it.
-                if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task " + task,
+                ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to task %s "
+                                + "callers: %s", r, task,
                         new RuntimeException("here").fillInStackTrace());
                 rTask.positionChildAtTop(r);
                 ActivityOptions.abort(options);
@@ -6401,8 +6392,8 @@
         task = activityTask;
 
         // Slot the activity into the history stack and proceed
-        if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,
-                new RuntimeException("here").fillInStackTrace());
+        ProtoLog.i(WM_DEBUG_ADD_REMOVE, "Adding activity %s to stack to task %s "
+                        + "callers: %s", r, task, new RuntimeException("here").fillInStackTrace());
         task.positionChildAtTop(r);
 
         // The transition animation and starting window are not needed if {@code allowMoveToFront}
@@ -7594,7 +7585,11 @@
         // Do not sleep activities in this stack if we're marked as focused and the keyguard
         // is in the process of going away.
         if (isFocusedStackOnDisplay()
-                && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) {
+                && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()
+                // Avoid resuming activities on secondary displays since we don't want bubble
+                // activities to be resumed while bubble is still collapsed.
+                // TODO(b/113840485): Having keyguard going away state for secondary displays.
+                && display.isDefaultDisplay) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 04d134c..1b779c6 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -20,7 +20,10 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
 import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_CONFIGS;
 import static com.android.server.wm.WindowOrganizerController.CONTROLLABLE_WINDOW_CONFIGS;
 
@@ -35,13 +38,13 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.view.SurfaceControl;
 import android.window.ITaskOrganizer;
 import android.window.ITaskOrganizerController;
 import android.window.WindowContainerToken;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 
 import java.io.PrintWriter;
@@ -49,6 +52,7 @@
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Set;
 import java.util.WeakHashMap;
 import java.util.function.Consumer;
 
@@ -58,7 +62,6 @@
  */
 class TaskOrganizerController extends ITaskOrganizerController.Stub {
     private static final String TAG = "TaskOrganizerController";
-    private static final LinkedList<IBinder> EMPTY_LIST = new LinkedList<>();
 
     /**
      * Masks specifying which configurations are important to report back to an organizer when
@@ -67,6 +70,16 @@
     private static final int REPORT_CONFIGS = CONTROLLABLE_CONFIGS;
     private static final int REPORT_WINDOW_CONFIGS = CONTROLLABLE_WINDOW_CONFIGS;
 
+    // The set of modes that are currently supports
+    // TODO: Remove once the task organizer can support all modes
+    @VisibleForTesting
+    static final int[] SUPPORTED_WINDOWING_MODES = {
+            WINDOWING_MODE_PINNED,
+            WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+            WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+            WINDOWING_MODE_MULTI_WINDOW,
+    };
+
     private final WindowManagerGlobalLock mGlobalLock;
 
     private class DeathRecipient implements IBinder.DeathRecipient {
@@ -113,6 +126,7 @@
         }
 
         void onTaskAppeared(Task task) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task appeared taskId=%d", task.mTaskId);
             final boolean visible = task.isVisible();
             final RunningTaskInfo taskInfo = task.getTaskInfo();
             mDeferTaskOrgCallbacksConsumer.accept(() -> {
@@ -134,6 +148,7 @@
 
 
         void onTaskVanished(Task task) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task vanished taskId=%d", task.mTaskId);
             final RunningTaskInfo taskInfo = task.getTaskInfo();
             mDeferTaskOrgCallbacksConsumer.accept(() -> {
                 try {
@@ -150,6 +165,7 @@
                 // by the organizer that don't receive that signal
                 return;
             }
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task info changed taskId=%d", task.mTaskId);
             mDeferTaskOrgCallbacksConsumer.accept(() -> {
                 if (!task.isOrganized()) {
                     // This is safe to ignore if the task is no longer organized
@@ -164,6 +180,8 @@
         }
 
         void onBackPressedOnTaskRoot(Task task) {
+            ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task back pressed on root taskId=%d",
+                    task.mTaskId);
             if (!task.mCreatedByOrganizer && !task.mTaskAppearedSent) {
                 // Skip if the task has not yet received taskAppeared(), except for tasks created
                 // by the organizer that don't receive that signal
@@ -233,9 +251,7 @@
 
         void dispose() {
             // Move organizer from managing specific windowing modes
-            for (int i = mTaskOrganizersForWindowingMode.size() - 1; i >= 0; --i) {
-                mTaskOrganizersForWindowingMode.valueAt(i).remove(mOrganizer.getBinder());
-            }
+            mTaskOrganizers.remove(mOrganizer.mTaskOrganizer);
 
             // Update tasks currently managed by this organizer to the next one available if
             // possible.
@@ -257,8 +273,8 @@
         }
     }
 
-    private final SparseArray<LinkedList<IBinder>> mTaskOrganizersForWindowingMode =
-            new SparseArray<>();
+    // List of task organizers by priority
+    private final LinkedList<ITaskOrganizer> mTaskOrganizers = new LinkedList<>();
     private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>();
     private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
     private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>();
@@ -285,59 +301,30 @@
     public void setDeferTaskOrgCallbacksConsumer(Consumer<Runnable> consumer) {
         mDeferTaskOrgCallbacksConsumer = consumer;
     }
-
     /**
-     * Register a TaskOrganizer to manage tasks as they enter the given windowing mode.
-     * If there was already a TaskOrganizer for this windowing mode it will be evicted
-     * but will continue to organize it's existing tasks.
+     * Register a TaskOrganizer to manage tasks as they enter the a supported windowing mode.
      */
     @Override
-    public void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
-        if (windowingMode == WINDOWING_MODE_PINNED) {
-            if (!mService.mSupportsPictureInPicture) {
-                throw new UnsupportedOperationException("Picture in picture is not supported on "
-                        + "this device");
-            }
-        } else if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
-            if (!mService.mSupportsSplitScreenMultiWindow) {
-                throw new UnsupportedOperationException("Split-screen is not supported on this "
-                        + "device");
-            }
-        } else if (windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
-            if (!mService.mSupportsMultiWindow) {
-                throw new UnsupportedOperationException("Multi-window is not supported on this "
-                        + "device");
-            }
-        } else {
-            throw new UnsupportedOperationException("As of now only Pinned/Split/Multiwindow"
-                    + " windowing modes are supported for registerTaskOrganizer");
-        }
+    public void registerTaskOrganizer(ITaskOrganizer organizer) {
         enforceStackPermission("registerTaskOrganizer()");
         final int uid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                if (getTaskOrganizer(windowingMode) != null) {
-                    Slog.w(TAG, "Task organizer already exists for windowing mode: "
-                            + windowingMode);
-                }
-
-                LinkedList<IBinder> orgs = mTaskOrganizersForWindowingMode.get(windowingMode);
-                if (orgs == null) {
-                    orgs = new LinkedList<>();
-                    mTaskOrganizersForWindowingMode.put(windowingMode, orgs);
-                }
-                orgs.add(organizer.asBinder());
-                if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
-                    mTaskOrganizerStates.put(organizer.asBinder(),
-                            new TaskOrganizerState(organizer, uid));
-                }
-
-                mService.mRootWindowContainer.forAllTasks((task) -> {
-                    if (task.getWindowingMode() == windowingMode) {
-                        task.updateTaskOrganizerState(true /* forceUpdate */);
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d",
+                        organizer.asBinder(), uid);
+                for (int winMode : SUPPORTED_WINDOWING_MODES) {
+                    if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) {
+                        mTaskOrganizers.add(organizer);
+                        mTaskOrganizerStates.put(organizer.asBinder(),
+                                new TaskOrganizerState(organizer, uid));
                     }
-                });
+                    mService.mRootWindowContainer.forAllTasks((task) -> {
+                        if (task.getWindowingMode() == winMode) {
+                            task.updateTaskOrganizerState(true /* forceUpdate */);
+                        }
+                    });
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -347,6 +334,7 @@
     @Override
     public void unregisterTaskOrganizer(ITaskOrganizer organizer) {
         enforceStackPermission("unregisterTaskOrganizer()");
+        final int uid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
@@ -354,6 +342,8 @@
                 if (state == null) {
                     return;
                 }
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Unregister task organizer=%s uid=%d",
+                        organizer.asBinder(), uid);
                 state.unlinkDeath();
                 state.dispose();
             }
@@ -362,17 +352,22 @@
         }
     }
 
+    /**
+     * @return the task organizer key for a given windowing mode.
+     */
     ITaskOrganizer getTaskOrganizer(int windowingMode) {
-        final IBinder organizer =
-                mTaskOrganizersForWindowingMode.get(windowingMode, EMPTY_LIST).peekLast();
-        if (organizer == null) {
-            return null;
+        return isSupportedWindowingMode(windowingMode)
+                ? mTaskOrganizers.peekLast()
+                : null;
+    }
+
+    private boolean isSupportedWindowingMode(int winMode) {
+        for (int i = 0; i < SUPPORTED_WINDOWING_MODES.length; i++) {
+            if (SUPPORTED_WINDOWING_MODES[i] == winMode) {
+                return true;
+            }
         }
-        final TaskOrganizerState state = mTaskOrganizerStates.get(organizer);
-        if (state == null) {
-            return null;
-        }
-        return state.mOrganizer.mTaskOrganizer;
+        return false;
     }
 
     void onTaskAppeared(ITaskOrganizer organizer, Task task) {
@@ -398,6 +393,8 @@
                     return null;
                 }
 
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Create root task displayId=%d winMode=%d",
+                        displayId, windowingMode);
                 final Task task = display.getDefaultTaskDisplayArea().createStack(windowingMode,
                         ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(),
                         true /* createdByOrganizer */);
@@ -422,6 +419,9 @@
                     throw new IllegalArgumentException(
                             "Attempt to delete task not created by organizer task=" + task);
                 }
+
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Delete root task display=%d winMode=%d",
+                        task.getDisplayId(), task.getWindowingMode());
                 task.removeImmediately();
                 return true;
             }
@@ -458,6 +458,8 @@
                 || mTmpTaskInfo.topActivityType != lastInfo.topActivityType
                 || mTmpTaskInfo.isResizeable != lastInfo.isResizeable
                 || mTmpTaskInfo.pictureInPictureParams != lastInfo.pictureInPictureParams
+                || mTmpTaskInfo.getConfiguration().windowConfiguration.getWindowingMode()
+                        != lastInfo.getConfiguration().windowConfiguration.getWindowingMode()
                 || !TaskDescription.equals(mTmpTaskInfo.taskDescription, lastInfo.taskDescription);
         if (!changed) {
             int cfgChanges = mTmpTaskInfo.configuration.diff(lastInfo.configuration);
@@ -627,6 +629,8 @@
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set intercept back pressed on root=%b",
+                        interceptBackPressed);
                 final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder());
                 if (state != null) {
                     state.setInterceptBackPressedOnTaskRoot(interceptBackPressed);
@@ -655,18 +659,19 @@
         final String innerPrefix = prefix + "  ";
         pw.print(prefix); pw.println("TaskOrganizerController:");
         pw.print(innerPrefix); pw.println("Per windowing mode:");
-        for (int i = 0; i < mTaskOrganizersForWindowingMode.size(); i++) {
-            final int windowingMode = mTaskOrganizersForWindowingMode.keyAt(i);
-            final List<IBinder> taskOrgs = mTaskOrganizersForWindowingMode.valueAt(i);
+        for (int i = 0; i < SUPPORTED_WINDOWING_MODES.length; i++) {
+            final int windowingMode = SUPPORTED_WINDOWING_MODES[i];
             pw.println(innerPrefix + "  "
                     + WindowConfiguration.windowingModeToString(windowingMode) + ":");
-            for (int j = 0; j < taskOrgs.size(); j++) {
-                final TaskOrganizerState state =  mTaskOrganizerStates.get(taskOrgs.get(j));
+            for (final TaskOrganizerState state : mTaskOrganizerStates.values()) {
                 final ArrayList<Task> tasks = state.mOrganizedTasks;
                 pw.print(innerPrefix + "    ");
                 pw.println(state.mOrganizer.mTaskOrganizer + " uid=" + state.mUid + ":");
                 for (int k = 0; k < tasks.size(); k++) {
-                    pw.println(innerPrefix + "      " + tasks.get(k));
+                    final Task task = tasks.get(k);
+                    if (windowingMode == task.getWindowingMode()) {
+                        pw.println(innerPrefix + "      " + task);
+                    }
                 }
             }
 
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index a66cd84..f32781a 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -242,8 +242,8 @@
         mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG;
         mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
         mDragWindowHandle.visible = true;
-        mDragWindowHandle.canReceiveKeys = false;
-        mDragWindowHandle.hasFocus = true;
+        // When dragging the window around, we do not want to steal focus for the window.
+        mDragWindowHandle.focusable = false;
         mDragWindowHandle.hasWallpaper = false;
         mDragWindowHandle.paused = false;
         mDragWindowHandle.ownerPid = Process.myPid();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index dbbb7ff..e3112ef 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -483,7 +483,7 @@
         final InsetsState insetsState =
                 new InsetsState(insetsPolicy.getInsetsForDispatch(mainWindow));
         mergeInsetsSources(insetsState, mainWindow.getRequestedInsetsState());
-        final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrameLw(), insetsState);
+        final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState);
         final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
                 attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
                 mHighResTaskSnapshotScale, insetsState);
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 6e00ab4..ce13867 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -308,7 +308,7 @@
         float defaultWallpaperX = wallpaperWin.isRtl() ? 1f : 0f;
         float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : defaultWallpaperX;
         float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f;
-        int availw = wallpaperWin.getFrameLw().right - wallpaperWin.getFrameLw().left - dw;
+        int availw = wallpaperWin.getFrame().right - wallpaperWin.getFrame().left - dw;
         int offset = availw > 0 ? -(int)(availw * wpx + .5f) : 0;
         if (mLastWallpaperDisplayOffsetX != Integer.MIN_VALUE) {
             offset += mLastWallpaperDisplayOffsetX;
@@ -323,7 +323,7 @@
 
         float wpy = mLastWallpaperY >= 0 ? mLastWallpaperY : 0.5f;
         float wpys = mLastWallpaperYStep >= 0 ? mLastWallpaperYStep : -1.0f;
-        int availh = wallpaperWin.getFrameLw().bottom - wallpaperWin.getFrameLw().top - dh;
+        int availh = wallpaperWin.getFrame().bottom - wallpaperWin.getFrame().top - dh;
         offset = availh > 0 ? -(int)(availh * wpy + .5f) : 0;
         if (mLastWallpaperDisplayOffsetY != Integer.MIN_VALUE) {
             offset += mLastWallpaperDisplayOffsetY;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index bf900f7..c45ccb6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1848,7 +1848,7 @@
         }
         // We use the visible frame, because we want the animation to morph the window from what
         // was visible to the user to the final destination of the new window.
-        Rect frame = replacedWindow.getVisibleFrameLw();
+        Rect frame = replacedWindow.getVisibleFrame();
         // We treat this as if this activity was opening, so we can trigger the app transition
         // animation and piggy-back on existing transition animation infrastructure.
         final DisplayContent dc = activity.getDisplayContent();
@@ -2069,7 +2069,7 @@
                 outDisplayFrame.setEmpty();
                 return;
             }
-            outDisplayFrame.set(win.getDisplayFrameLw());
+            outDisplayFrame.set(win.getDisplayFrame());
             if (win.inSizeCompatMode()) {
                 outDisplayFrame.scale(win.mInvGlobalScale);
             }
@@ -2389,7 +2389,7 @@
             if (displayPolicy.areSystemBarsForcedShownLw(win)) {
                 result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
             }
-            if (!win.isGoneForLayoutLw()) {
+            if (!win.isGoneForLayout()) {
                 win.mResizedWhileGone = false;
             }
 
@@ -2419,7 +2419,7 @@
             win.getInsetsForRelayout(outContentInsets, outVisibleInsets,
                     outStableInsets);
             outCutout.set(win.getWmDisplayCutout().getDisplayCutout());
-            outBackdropFrame.set(win.getBackdropFrame(win.getFrameLw()));
+            outBackdropFrame.set(win.getBackdropFrame(win.getFrame()));
             outInsetsState.set(win.getInsetsState(), win.isClientLocal());
             if (DEBUG) {
                 Slog.v(TAG_WM, "Relayout given client " + client.asBinder()
@@ -2883,11 +2883,6 @@
     }
 
     @Override
-    public WindowManagerPolicy.WindowState getInputMethodWindowLw() {
-        return mRoot.getCurrentInputMethodWindow();
-    }
-
-    @Override
     public void notifyKeyguardTrustedChanged() {
         mAtmInternal.notifyKeyguardTrustedChanged();
     }
@@ -5481,7 +5476,7 @@
                     // Window has been removed or hidden; no draw will now happen, so stop waiting.
                     ProtoLog.w(WM_DEBUG_SCREEN_ON, "Aborted waiting for drawn: %s", win);
                     container.mWaitingForDrawn.remove(win);
-                } else if (win.hasDrawnLw()) {
+                } else if (win.hasDrawn()) {
                     // Window is now drawn (and shown).
                     ProtoLog.d(WM_DEBUG_SCREEN_ON, "Window drawn win=%s", win);
                     container.mWaitingForDrawn.remove(win);
@@ -7356,7 +7351,7 @@
             synchronized (mGlobalLock) {
                 WindowState windowState = mWindowMap.get(token);
                 if (windowState != null) {
-                    outBounds.set(windowState.getFrameLw());
+                    outBounds.set(windowState.getFrame());
                 } else {
                     outBounds.setEmpty();
                 }
@@ -8076,8 +8071,7 @@
         h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | sanitizedFlags;
         h.layoutParamsType = type;
         h.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
-        h.canReceiveKeys = false;
-        h.hasFocus = false;
+        h.focusable = false;
         h.hasWallpaper = false;
         h.paused = false;
 
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index d25a648..c7cad2f 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 import static android.Manifest.permission.READ_FRAME_BUFFER;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED;
 import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG;
@@ -45,6 +46,7 @@
 import android.window.WindowContainerTransaction;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.function.pooled.PooledConsumer;
 import com.android.internal.util.function.pooled.PooledLambda;
 
@@ -127,6 +129,8 @@
                 if (callback != null) {
                     syncId = startSyncWithOrganizer(callback);
                 }
+                ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d",
+                        syncId);
                 mService.deferWindowLayout();
                 try {
                     ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
@@ -427,6 +431,7 @@
 
     @VisibleForTesting
     void setSyncReady(int id) {
+        ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Set sync ready, syncId=%d", id);
         mBLASTSyncEngine.setReady(id);
     }
 
@@ -436,9 +441,10 @@
     }
 
     @Override
-    public void onTransactionReady(int mSyncId, Set<WindowContainer> windowContainersReady) {
+    public void onTransactionReady(int syncId, Set<WindowContainer> windowContainersReady) {
+        ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Transaction ready, syncId=%d", syncId);
         final IWindowContainerTransactionCallback callback =
-                mTransactionCallbacksByPendingSyncId.get(mSyncId);
+                mTransactionCallbacksByPendingSyncId.get(syncId);
 
         SurfaceControl.Transaction mergedTransaction = new SurfaceControl.Transaction();
         for (WindowContainer container : windowContainersReady) {
@@ -446,14 +452,14 @@
         }
 
         try {
-            callback.onTransactionReady(mSyncId, mergedTransaction);
+            callback.onTransactionReady(syncId, mergedTransaction);
         } catch (RemoteException e) {
             // If there's an exception when trying to send the mergedTransaction to the client, we
             // should immediately apply it here so the transactions aren't lost.
             mergedTransaction.apply();
         }
 
-        mTransactionCallbacksByPendingSyncId.remove(mSyncId);
+        mTransactionCallbacksByPendingSyncId.remove(syncId);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index c714eeb..c5ebace 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -22,9 +22,9 @@
 import static android.os.IInputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
 import static android.view.Display.INVALID_DISPLAY;
 
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerService.MY_PID;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_CONFIGURATION;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_RELEASE;
@@ -72,6 +72,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.Watchdog;
 import com.android.server.wm.ActivityTaskManagerService.HotPath;
@@ -941,7 +942,7 @@
                 final ActivityRecord r = candidates.remove(0);
                 if (DEBUG_RELEASE) Slog.v(TAG_RELEASE, "Destroying " + r
                         + " in state " + r.getState() + " for reason " + reason);
-                r.destroyImmediately(true /*removeFromApp*/, reason);
+                r.destroyImmediately(reason);
                 --maxRelease;
             } while (maxRelease > 0);
         }
@@ -1348,9 +1349,8 @@
             }
             return;
         }
-        if (DEBUG_CONFIGURATION) {
-            Slog.v(TAG_CONFIGURATION, "Sending to proc " + mName + " new config " + config);
-        }
+        ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s new config %s", mName,
+                config);
         if (Build.IS_DEBUGGABLE && mHasImeService) {
             // TODO (b/135719017): Temporary log for debugging IME service.
             Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName + " new config " + config);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0e455d2..9ff33b1 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1009,8 +1009,8 @@
         getDisplayContent().reapplyMagnificationSpec();
     }
 
-    @Override
-    public int getOwningUid() {
+    /** Returns the uid of the app that owns this window. */
+    int getOwningUid() {
         return mOwnerUid;
     }
 
@@ -1024,8 +1024,12 @@
         return mOwnerCanAddInternalSystemWindow;
     }
 
-    @Override
-    public boolean canAcquireSleepToken() {
+    /**
+     * Returns {@code true} if the window owner has the permission to acquire a sleep token when
+     * it's visible. That is, they have the permission
+     * {@link androidManifest.permission#DEVICE_POWER}.
+     */
+    boolean canAcquireSleepToken() {
         return mSession.mCanAcquireSleepToken;
     }
 
@@ -1046,7 +1050,7 @@
 
     void computeFrame(DisplayFrames displayFrames) {
         getLayoutingWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
-        computeFrameLw();
+        computeFrame();
         // Update the source frame to provide insets to other windows during layout. If the
         // simulated frames exist, then this is not computing a stable result so just skip.
         if (mControllableInsetProvider != null && mSimulatedWindowFrames == null) {
@@ -1054,8 +1058,10 @@
         }
     }
 
-    @Override
-    public void computeFrameLw() {
+    /**
+     * Perform standard frame computation. The result can be obtained with getFrame() if so desired.
+     */
+    void computeFrame() {
         if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) {
             // This window is being replaced and either already got information that it's being
             // removed or we are still waiting for some information. Because of this we don't
@@ -1283,32 +1289,41 @@
         }
     }
 
-    @Override
-    public Rect getFrameLw() {
+    /** Retrieves the current frame of the window that the application sees. */
+    Rect getFrame() {
         return mWindowFrames.mFrame;
     }
 
     /** Accessor for testing */
-    Rect getRelativeFrameLw() {
+    Rect getRelativeFrame() {
         return mWindowFrames.mRelFrame;
     }
 
-    @Override
-    public Rect getDisplayFrameLw() {
+    /** Retrieves the frame of the display that this window was last laid out in. */
+    Rect getDisplayFrame() {
         return mWindowFrames.mDisplayFrame;
     }
 
-    @Override
-    public Rect getContentFrameLw() {
+    /**
+     * Retrieves the frame of the content area that this window was last laid out in. This is the
+     * area in which the content of the window should be placed. It will be smaller than the display
+     * frame to account for screen decorations such as a status bar or soft keyboard.
+     */
+    Rect getContentFrame() {
         return mWindowFrames.mContentFrame;
     }
 
-    @Override
-    public Rect getVisibleFrameLw() {
+    /**
+     * Retrieves the frame of the visible area that this window was last laid out in. This is the
+     * area of the screen in which the window will actually be fully visible. It will be smaller
+     * than the content frame to account for transient UI elements blocking it such as an input
+     * method's candidates UI.
+     */
+    Rect getVisibleFrame() {
         return mWindowFrames.mVisibleFrame;
     }
 
-    Rect getStableFrameLw() {
+    Rect getStableFrame() {
         return mWindowFrames.mStableFrame;
     }
 
@@ -1337,32 +1352,17 @@
     }
 
     @Override
-    public boolean getGivenInsetsPendingLw() {
-        return mGivenInsetsPending;
-    }
-
-    @Override
-    public Rect getGivenContentInsetsLw() {
-        return mGivenContentInsets;
-    }
-
-    @Override
-    public Rect getGivenVisibleInsetsLw() {
-        return mGivenVisibleInsets;
-    }
-
-    @Override
     public WindowManager.LayoutParams getAttrs() {
         return mAttrs;
     }
 
-    @Override
-    public int getSystemUiVisibility() {
+    /** Retrieves the current system UI visibility flags associated with this window. */
+    int getSystemUiVisibility() {
         return mSystemUiVisibility;
     }
 
-    @Override
-    public int getSurfaceLayer() {
+    /** Gets the layer at which this window's surface will be Z-ordered. */
+    int getSurfaceLayer() {
         return mLayer;
     }
 
@@ -1376,8 +1376,8 @@
         return mActivityRecord != null ? mActivityRecord.appToken : null;
     }
 
-    @Override
-    public boolean isVoiceInteraction() {
+    /** Returns true if this window is participating in voice interaction. */
+    boolean isVoiceInteraction() {
         return mActivityRecord != null && mActivityRecord.mVoiceInteraction;
     }
 
@@ -1391,7 +1391,7 @@
      */
     void updateResizingWindowIfNeeded() {
         final WindowStateAnimator winAnimator = mWinAnimator;
-        if (!mHasSurface || getDisplayContent().mLayoutSeq != mLayoutSeq || isGoneForLayoutLw()) {
+        if (!mHasSurface || getDisplayContent().mLayoutSeq != mLayoutSeq || isGoneForLayout()) {
             return;
         }
 
@@ -1464,7 +1464,7 @@
                 mWmService.mResizingWindows.add(this);
             }
         } else if (getOrientationChanging()) {
-            if (isDrawnLw()) {
+            if (isDrawn()) {
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
                         "Orientation not waiting for draw in %s, surfaceController %s", this,
                         winAnimator.mSurfaceController);
@@ -1639,9 +1639,16 @@
                 : DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
     }
 
-    @Override
-    public boolean hasAppShownWindows() {
-        return mActivityRecord != null && (mActivityRecord.firstWindowDrawn || mActivityRecord.startingDisplayed);
+    /**
+     * Returns true if, at any point, the application token associated with this window has actually
+     * displayed any windows. This is most useful with the "starting up" window to determine if any
+     * windows were displayed when it is closed.
+     *
+     * @return {@code true} if one or more windows have been displayed, else false.
+     */
+    boolean hasAppShownWindows() {
+        return mActivityRecord != null
+                && (mActivityRecord.firstWindowDrawn || mActivityRecord.startingDisplayed);
     }
 
     boolean isIdentityMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
@@ -1663,7 +1670,7 @@
 
     @Override
     boolean hasContentToDisplay() {
-        if (!mAppFreezing && isDrawnLw() && (mViewVisibility == View.VISIBLE
+        if (!mAppFreezing && isDrawn() && (mViewVisibility == View.VISIBLE
                 || (isAnimating(TRANSITION | PARENTS)
                 && !getDisplayContent().mAppTransition.isTransitionSet()))) {
             return true;
@@ -1851,10 +1858,9 @@
      * Like isOnScreen, but returns false if the surface hasn't yet
      * been drawn.
      */
-    @Override
-    public boolean isDisplayedLw() {
+    boolean isDisplayed() {
         final ActivityRecord atoken = mActivityRecord;
-        return isDrawnLw() && isVisibleByPolicy()
+        return isDrawn() && isVisibleByPolicy()
                 && ((!isParentWindowHidden() && (atoken == null || atoken.mVisibleRequested))
                         || isAnimating(TRANSITION | PARENTS));
     }
@@ -1867,8 +1873,8 @@
         return isAnimating(TRANSITION | PARENTS);
     }
 
-    @Override
-    public boolean isGoneForLayoutLw() {
+    /** Returns {@code true} if this window considered to be gone for purposes of layout. */
+    boolean isGoneForLayout() {
         final ActivityRecord atoken = mActivityRecord;
         return mViewVisibility == View.GONE
                 || !mRelayoutCalled
@@ -1895,11 +1901,11 @@
     }
 
     /**
-     * Returns true if the window has a surface that it has drawn a
-     * complete UI in to.
+     * Returns true if the window has a surface that it has drawn a complete UI in to. Note that
+     * this is different from {@link #hasDrawn()} in that it also returns true if the window is
+     * READY_TO_SHOW, but was not yet promoted to HAS_DRAWN.
      */
-    @Override
-    public boolean isDrawnLw() {
+    boolean isDrawn() {
         return mHasSurface && !mDestroying &&
                 (mWinAnimator.mDrawState == READY_TO_SHOW || mWinAnimator.mDrawState == HAS_DRAWN);
     }
@@ -1914,7 +1920,7 @@
         // to determine if it's occluding apps.
         return ((!mIsWallpaper && mAttrs.format == PixelFormat.OPAQUE)
                 || (mIsWallpaper && mWallpaperVisible))
-                && isDrawnLw() && !isAnimating(TRANSITION | PARENTS);
+                && isDrawn() && !isAnimating(TRANSITION | PARENTS);
     }
 
     /** @see WindowManagerInternal#waitForAllWindowsDrawn */
@@ -1929,7 +1935,7 @@
                 return;
             }
             if (mAttrs.type == TYPE_APPLICATION_STARTING) {
-                if (isDrawnLw()) {
+                if (isDrawn()) {
                     // Unnecessary to redraw a drawn starting window.
                     return;
                 }
@@ -2016,11 +2022,11 @@
     @Override
     void onResize() {
         final ArrayList<WindowState> resizingWindows = mWmService.mResizingWindows;
-        if (mHasSurface && !isGoneForLayoutLw() && !resizingWindows.contains(this)) {
+        if (mHasSurface && !isGoneForLayout() && !resizingWindows.contains(this)) {
             ProtoLog.d(WM_DEBUG_RESIZE, "onResize: Resizing %s", this);
             resizingWindows.add(this);
         }
-        if (isGoneForLayoutLw()) {
+        if (isGoneForLayout()) {
             mResizedWhileGone = true;
         }
 
@@ -2058,10 +2064,17 @@
         // animating... let's do something.
         final int left = mWindowFrames.mFrame.left;
         final int top = mWindowFrames.mFrame.top;
+
+        // During the transition from pip to fullscreen, the activity windowing mode is set to
+        // fullscreen at the beginning while the task is kept in pinned mode. Skip the move
+        // animation in such case since the transition is handled in SysUI.
+        final boolean hasMovementAnimation = getTask() == null
+                ? getWindowConfiguration().hasMovementAnimations()
+                : getTask().getWindowConfiguration().hasMovementAnimations();
         if (mToken.okToAnimate()
                 && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
                 && !isDragResizing()
-                && getWindowConfiguration().hasMovementAnimations()
+                && hasMovementAnimation
                 && !mWinAnimator.mLastHidden
                 && !mSeamlesslyRotated) {
             startMoveAnimation(left, top);
@@ -2507,7 +2520,7 @@
 
     /** Returns true if the replacement window was removed. */
     boolean removeReplacedWindowIfNeeded(WindowState replacement) {
-        if (mWillReplaceWindow && mReplacementWindow == replacement && replacement.hasDrawnLw()) {
+        if (mWillReplaceWindow && mReplacementWindow == replacement && replacement.hasDrawn()) {
             replacement.mSkipEnterAnimationForSeamlessReplacement = false;
             removeReplacedWindow();
             return true;
@@ -2741,7 +2754,7 @@
             mLayoutNeeded = true;
         }
 
-        if (isDrawnLw() && mToken.okToAnimate()) {
+        if (isDrawn() && mToken.okToAnimate()) {
             mWinAnimator.applyEnterAnimationLocked();
         }
     }
@@ -2866,8 +2879,8 @@
         return getWindowConfiguration().keepVisibleDeadAppWindowOnScreen();
     }
 
-    @Override
-    public boolean canReceiveKeys() {
+    /** Returns {@code true} if this window desires key events. */
+    boolean canReceiveKeys() {
         return canReceiveKeys(false /* fromUserTouch */);
     }
 
@@ -2916,8 +2929,13 @@
                 && recentsAnimationController.shouldApplyInputConsumer(mActivityRecord);
     }
 
-    @Override
-    public boolean hasDrawnLw() {
+    /**
+     * Returns {@code true} if this window has been shown on screen at some time in the past.
+     *
+     * @deprecated Use {@link #isDrawnLw} or any of the other drawn/visibility methods.
+     */
+    @Deprecated
+    boolean hasDrawn() {
         return mWinAnimator.mDrawState == WindowStateAnimator.HAS_DRAWN;
     }
 
@@ -3151,8 +3169,8 @@
         }
     }
 
-    @Override
-    public boolean isAlive() {
+    /** Checks whether the process hosting this window is currently alive. */
+    boolean isAlive() {
         return mClient.asBinder().isBinderAlive();
     }
 
@@ -3332,16 +3350,6 @@
         mLastExclusionLogUptimeMillis[EXCLUSION_RIGHT] = now;
     }
 
-    @Override
-    public boolean isDefaultDisplay() {
-        final DisplayContent displayContent = getDisplayContent();
-        if (displayContent == null) {
-            // Only a window that was on a non-default display can be detached from it.
-            return false;
-        }
-        return displayContent.isDefaultDisplay;
-    }
-
     /** @return {@code true} if this window can be shown to all users. */
     boolean showForAllUsers() {
 
@@ -3401,10 +3409,10 @@
             // All window frames that are fullscreen extend above status bar, but some don't extend
             // below navigation bar. Thus, check for display frame for top/left and stable frame for
             // bottom right.
-            if (win.getFrameLw().left <= win.getDisplayFrameLw().left
-                    && win.getFrameLw().top <= win.getDisplayFrameLw().top
-                    && win.getFrameLw().right >= win.getStableFrameLw().right
-                    && win.getFrameLw().bottom >= win.getStableFrameLw().bottom) {
+            if (win.getFrame().left <= win.getDisplayFrame().left
+                    && win.getFrame().top <= win.getDisplayFrame().top
+                    && win.getFrame().right >= win.getStableFrame().right
+                    && win.getFrame().bottom >= win.getStableFrame().bottom) {
                 // Is a fullscreen window, like the clock alarm. Show to everyone.
                 return true;
             }
@@ -3562,6 +3570,13 @@
     }
 
     void reportResized() {
+        // If the activity is scheduled to relaunch, skip sending the resized to ViewRootImpl now
+        // since it will be destroyed anyway. This also prevents the client from receiving
+        // windowing mode change before it is destroyed.
+        if (mActivityRecord != null && mActivityRecord.isRelaunching()) {
+            return;
+        }
+
         if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wm.reportResized_" + getWindowTag());
         }
@@ -3767,11 +3782,11 @@
      *          is transitioning into/out-of fullscreen. */
     boolean isLetterboxedAppWindow() {
         return !inMultiWindowMode() && !matchesDisplayBounds()
-                || isLetterboxedForDisplayCutoutLw();
+                || isLetterboxedForDisplayCutout();
     }
 
-    @Override
-    public boolean isLetterboxedForDisplayCutoutLw() {
+    /** Returns {@code true} if the window is letterboxed for the display cutout. */
+    boolean isLetterboxedForDisplayCutout() {
         if (mActivityRecord == null) {
             // Only windows with an ActivityRecord are letterboxed.
             return false;
@@ -3875,7 +3890,7 @@
         // background.
         return (getDisplayContent().mDividerControllerLocked.isResizing()
                         || mActivityRecord != null && !mActivityRecord.mFrozenBounds.isEmpty()) &&
-                !task.inFreeformWindowingMode() && !isGoneForLayoutLw();
+                !task.inFreeformWindowingMode() && !isGoneForLayout();
 
     }
 
@@ -4291,7 +4306,7 @@
 
     private boolean isParentWindowGoneForLayout() {
         final WindowState parent = getParentWindow();
-        return parent != null && parent.isGoneForLayoutLw();
+        return parent != null && parent.isGoneForLayout();
     }
 
     void setWillReplaceWindow(boolean animate) {
@@ -4404,8 +4419,7 @@
         return null;
     }
 
-    @Override
-    public int getRotationAnimationHint() {
+    int getRotationAnimationHint() {
         if (mActivityRecord != null) {
             return mActivityRecord.mRotationAnimationHint;
         } else {
@@ -4861,7 +4875,7 @@
     }
 
     boolean hasVisibleNotDrawnWallpaper() {
-        if (mWallpaperVisible && !isDrawnLw()) {
+        if (mWallpaperVisible && !isDrawn()) {
             return true;
         }
         for (int j = mChildren.size() - 1; j >= 0; --j) {
@@ -4885,9 +4899,9 @@
             return;
         }
         if (DEBUG_VISIBILITY) {
-            Slog.v(TAG, "Win " + this + ": isDrawn=" + isDrawnLw()
+            Slog.v(TAG, "Win " + this + ": isDrawn=" + isDrawn()
                     + ", animating=" + isAnimating(TRANSITION | PARENTS));
-            if (!isDrawnLw()) {
+            if (!isDrawn()) {
                 Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceController
                         + " pv=" + isVisibleByPolicy()
                         + " mDrawState=" + mWinAnimator.mDrawState
@@ -4898,7 +4912,7 @@
         }
 
         results.numInteresting++;
-        if (isDrawnLw()) {
+        if (isDrawn()) {
             results.numDrawn++;
             if (!isAnimating(TRANSITION | PARENTS)) {
                 results.numVisible++;
@@ -5034,7 +5048,7 @@
     int relayoutVisibleWindow(int result, int attrChanges) {
         final boolean wasVisible = isVisibleLw();
 
-        result |= (!wasVisible || !isDrawnLw()) ? RELAYOUT_RES_FIRST_TIME : 0;
+        result |= (!wasVisible || !isDrawn()) ? RELAYOUT_RES_FIRST_TIME : 0;
 
         if (mAnimatingExit) {
             Slog.d(TAG, "relayoutVisibleWindow: " + this + " mAnimatingExit=true, mRemoveOnExit="
@@ -5606,15 +5620,14 @@
         return !mTapExcludeRegion.isEmpty();
     }
 
-    @Override
-    public boolean isInputMethodTarget() {
+    boolean isInputMethodTarget() {
         return getDisplayContent().mInputMethodTarget == this;
     }
 
     long getFrameNumber() {
         // Return the frame number in which changes requested in this layout will be rendered or
         // -1 if we do not expect the frame to be rendered.
-        return getFrameLw().isEmpty() ? -1 : mFrameNumber;
+        return getFrame().isEmpty() ? -1 : mFrameNumber;
     }
 
     void setFrameNumber(long frameNumber) {
@@ -5677,8 +5690,8 @@
         return mWindowFrames.mVisibleInsets;
     }
 
-    @Override
-    public WindowFrames getWindowFrames() {
+    /** Returns the {@link WindowFrames} associated with this {@link WindowState}. */
+    WindowFrames getWindowFrames() {
         return mWindowFrames;
     }
 
@@ -5786,7 +5799,7 @@
         // won't exactly match the final freeform window frame (e.g. when overlapping with
         // the status bar). In that case we need to use the final frame.
         if (inFreeformWindowingMode()) {
-            outFrame.set(getFrameLw());
+            outFrame.set(getFrame());
         } else if (isLetterboxedAppWindow() || mToken.isFixedRotationTransforming()) {
             // 1. The letterbox surfaces should be animated with the owner activity, so use task
             //    bounds to include them.
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 92177ab..1bd712c 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -38,6 +38,7 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION;
 import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
 import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
@@ -525,13 +526,13 @@
 
         if (DEBUG) {
             Slog.v(TAG, "Got surface: " + mSurfaceController
-                    + ", set left=" + w.getFrameLw().left + " top=" + w.getFrameLw().top);
+                    + ", set left=" + w.getFrame().left + " top=" + w.getFrame().top);
         }
 
         if (SHOW_LIGHT_TRANSACTIONS) {
             Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked");
             WindowManagerService.logSurface(w, "CREATE pos=("
-                    + w.getFrameLw().left + "," + w.getFrameLw().top + ") ("
+                    + w.getFrame().left + "," + w.getFrame().top + ") ("
                     + width + "x" + height + ")" + " HIDE", false);
         }
 
@@ -659,80 +660,7 @@
     }
 
     void computeShownFrameLocked() {
-        final ScreenRotationAnimation screenRotationAnimation =
-                mWin.getDisplayContent().getRotationAnimation();
-        final boolean windowParticipatesInScreenRotationAnimation =
-                !mWin.mForceSeamlesslyRotate;
-        final boolean screenAnimation = screenRotationAnimation != null
-                && screenRotationAnimation.isAnimating()
-                && windowParticipatesInScreenRotationAnimation;
-
-        if (screenAnimation) {
-            // cache often used attributes locally
-            final Rect frame = mWin.getFrameLw();
-            final float tmpFloats[] = mService.mTmpFloats;
-            final Matrix tmpMatrix = mWin.mTmpMatrix;
-
-            // Compute the desired transformation.
-            if (screenRotationAnimation.isRotating()) {
-                // If we are doing a screen animation, the global rotation
-                // applied to windows can result in windows that are carefully
-                // aligned with each other to slightly separate, allowing you
-                // to see what is behind them.  An unsightly mess.  This...
-                // thing...  magically makes it call good: scale each window
-                // slightly (two pixels larger in each dimension, from the
-                // window's center).
-                final float w = frame.width();
-                final float h = frame.height();
-                if (w>=1 && h>=1) {
-                    tmpMatrix.setScale(1 + 2/w, 1 + 2/h, w/2, h/2);
-                } else {
-                    tmpMatrix.reset();
-                }
-            } else {
-                tmpMatrix.reset();
-            }
-
-            tmpMatrix.postScale(mWin.mGlobalScale, mWin.mGlobalScale);
-
-            // WindowState.prepareSurfaces expands for surface insets (in order they don't get
-            // clipped by the WindowState surface), so we need to go into the other direction here.
-            tmpMatrix.postTranslate(mWin.mAttrs.surfaceInsets.left,
-                    mWin.mAttrs.surfaceInsets.top);
-
-
-            // "convert" it into SurfaceFlinger's format
-            // (a 2x2 matrix + an offset)
-            // Here we must not transform the position of the surface
-            // since it is already included in the transformation.
-            //Slog.i(TAG_WM, "Transform: " + matrix);
-
-            mHaveMatrix = true;
-            tmpMatrix.getValues(tmpFloats);
-            mDsDx = tmpFloats[Matrix.MSCALE_X];
-            mDtDx = tmpFloats[Matrix.MSKEW_Y];
-            mDtDy = tmpFloats[Matrix.MSKEW_X];
-            mDsDy = tmpFloats[Matrix.MSCALE_Y];
-
-            // Now set the alpha...  but because our current hardware
-            // can't do alpha transformation on a non-opaque surface,
-            // turn it off if we are running an animation that is also
-            // transforming since it is more important to have that
-            // animation be smooth.
-            mShownAlpha = mAlpha;
-            if (!mService.mLimitedAlphaCompositing
-                    || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
-                    || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDtDy, mDsDy)))) {
-                mShownAlpha *= screenRotationAnimation.getEnterTransformation().getAlpha();
-            }
-
-            if ((DEBUG_ANIM || DEBUG) && (mShownAlpha == 1.0 || mShownAlpha == 0.0)) {
-                Slog.v(TAG, "computeShownFrameLocked: Animating " + this + " mAlpha=" + mAlpha
-                                + " screen=" + (screenAnimation
-                        ? screenRotationAnimation.getEnterTransformation().getAlpha() : "null"));
-            }
-            return;
-        } else if (mIsWallpaper && mService.mRoot.mWallpaperActionPending) {
+        if (mIsWallpaper && mService.mRoot.mWallpaperActionPending) {
             return;
         } else if (mWin.isDragResizeChanged()) {
             // This window is awaiting a relayout because user just started (or ended)
@@ -968,7 +896,7 @@
 
             // There is no need to wait for an animation change if our window is gone for layout
             // already as we'll never be visible.
-            if (w.getOrientationChanging() && w.isGoneForLayoutLw()) {
+            if (w.getOrientationChanging() && w.isGoneForLayout()) {
                 ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation change skips hidden %s", w);
                 w.setOrientationChanging(false);
             }
@@ -992,7 +920,7 @@
             // really hidden (gone for layout), there is no point in still waiting for it.
             // Note that this does introduce a potential glitch if the window becomes unhidden
             // before it has drawn for the new orientation.
-            if (w.getOrientationChanging() && w.isGoneForLayoutLw()) {
+            if (w.getOrientationChanging() && w.isGoneForLayout()) {
                 w.setOrientationChanging(false);
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
                         "Orientation change skips hidden %s", w);
@@ -1070,7 +998,7 @@
         }
 
         if (w.getOrientationChanging()) {
-            if (!w.isDrawnLw()) {
+            if (!w.isDrawn()) {
                 mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE;
                 mAnimator.mLastWindowFreezeSource = w;
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
@@ -1327,7 +1255,7 @@
             mWin.getDisplayContent().adjustForImeIfNeeded();
         }
 
-        return mWin.isAnimating(PARENTS);
+        return mWin.isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
     }
 
     void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index b3f3a5e..9aca848 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -40,14 +40,12 @@
 namespace android {
 
 static JavaVM* sJvm = nullptr;
-
 static jmethodID sMethodIdOnComplete;
-
 static struct {
     jfieldID id;
     jfieldID scale;
     jfieldID delay;
-} gPrimitiveClassInfo;
+} sPrimitiveClassInfo;
 
 static_assert(static_cast<uint8_t>(V1_0::EffectStrength::LIGHT) ==
                 static_cast<uint8_t>(aidl::EffectStrength::LIGHT));
@@ -77,100 +75,117 @@
 static_assert(static_cast<uint8_t>(V1_3::Effect::TEXTURE_TICK) ==
                 static_cast<uint8_t>(aidl::Effect::TEXTURE_TICK));
 
-static inline void callVibrationOnComplete(jobject vibration) {
-    if (vibration == nullptr) {
-        return;
+class NativeVibratorService {
+public:
+    NativeVibratorService(JNIEnv* env, jobject callbackListener)
+          : mController(std::make_unique<vibrator::HalController>()),
+            mCallbackListener(env->NewGlobalRef(callbackListener)) {
+        LOG_ALWAYS_FATAL_IF(mCallbackListener == nullptr,
+                            "Unable to create global reference to vibration callback handler");
     }
-    auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
-    jniEnv->CallVoidMethod(vibration, sMethodIdOnComplete);
-    jniEnv->DeleteGlobalRef(vibration);
-}
+
+    ~NativeVibratorService() {
+        auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+        jniEnv->DeleteGlobalRef(mCallbackListener);
+    }
+
+    vibrator::HalController* controller() const { return mController.get(); }
+
+    std::function<void()> createCallback(jlong vibrationId) {
+        return [vibrationId, this]() {
+            auto jniEnv = GetOrAttachJNIEnvironment(sJvm);
+            jniEnv->CallVoidMethod(mCallbackListener, sMethodIdOnComplete, vibrationId);
+        };
+    }
+
+private:
+    const std::unique_ptr<vibrator::HalController> mController;
+    const jobject mCallbackListener;
+};
 
 static aidl::CompositeEffect effectFromJavaPrimitive(JNIEnv* env, jobject primitive) {
     aidl::CompositeEffect effect;
     effect.primitive = static_cast<aidl::CompositePrimitive>(
-            env->GetIntField(primitive, gPrimitiveClassInfo.id));
-    effect.scale = static_cast<float>(env->GetFloatField(primitive, gPrimitiveClassInfo.scale));
-    effect.delayMs = static_cast<int32_t>(env->GetIntField(primitive, gPrimitiveClassInfo.delay));
+            env->GetIntField(primitive, sPrimitiveClassInfo.id));
+    effect.scale = static_cast<float>(env->GetFloatField(primitive, sPrimitiveClassInfo.scale));
+    effect.delayMs = static_cast<int32_t>(env->GetIntField(primitive, sPrimitiveClassInfo.delay));
     return effect;
 }
 
-static void destroyVibratorController(void* rawVibratorController) {
-    vibrator::HalController* vibratorController =
-            reinterpret_cast<vibrator::HalController*>(rawVibratorController);
-    if (vibratorController) {
-        delete vibratorController;
+static void destroyNativeService(void* servicePtr) {
+    NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
+    if (service) {
+        delete service;
     }
 }
 
-static jlong vibratorInit(JNIEnv* /* env */, jclass /* clazz */) {
-    std::unique_ptr<vibrator::HalController> controller =
-            std::make_unique<vibrator::HalController>();
-    controller->init();
-    return reinterpret_cast<jlong>(controller.release());
+static jlong vibratorInit(JNIEnv* env, jclass /* clazz */, jobject callbackListener) {
+    std::unique_ptr<NativeVibratorService> service =
+            std::make_unique<NativeVibratorService>(env, callbackListener);
+    service->controller()->init();
+    return reinterpret_cast<jlong>(service.release());
 }
 
 static jlong vibratorGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) {
-    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyVibratorController));
+    return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeService));
 }
 
-static jboolean vibratorExists(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
-    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
-    if (controller == nullptr) {
-        ALOGE("vibratorExists failed because controller was not initialized");
+static jboolean vibratorExists(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
+    NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("vibratorExists failed because native service was not initialized");
         return JNI_FALSE;
     }
-    return controller->ping().isOk() ? JNI_TRUE : JNI_FALSE;
+    return service->controller()->ping().isOk() ? JNI_TRUE : JNI_FALSE;
 }
 
-static void vibratorOn(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, jlong timeoutMs,
-                       jobject vibration) {
-    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
-    if (controller == nullptr) {
-        ALOGE("vibratorOn failed because controller was not initialized");
+static void vibratorOn(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong timeoutMs,
+                       jlong vibrationId) {
+    NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("vibratorOn failed because native service was not initialized");
         return;
     }
-    jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration);
-    auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); };
-    controller->on(std::chrono::milliseconds(timeoutMs), callback);
+    auto callback = service->createCallback(vibrationId);
+    service->controller()->on(std::chrono::milliseconds(timeoutMs), callback);
 }
 
-static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
-    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
-    if (controller == nullptr) {
-        ALOGE("vibratorOff failed because controller was not initialized");
+static void vibratorOff(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
+    NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("vibratorOff failed because native service was not initialized");
         return;
     }
-    controller->off();
+    service->controller()->off();
 }
 
-static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
+static void vibratorSetAmplitude(JNIEnv* env, jclass /* clazz */, jlong servicePtr,
                                  jint amplitude) {
-    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
-    if (controller == nullptr) {
-        ALOGE("vibratorSetAmplitude failed because controller was not initialized");
+    NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("vibratorSetAmplitude failed because native service was not initialized");
         return;
     }
-    controller->setAmplitude(static_cast<int32_t>(amplitude));
+    service->controller()->setAmplitude(static_cast<int32_t>(amplitude));
 }
 
-static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
+static void vibratorSetExternalControl(JNIEnv* env, jclass /* clazz */, jlong servicePtr,
                                        jboolean enabled) {
-    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
-    if (controller == nullptr) {
-        ALOGE("vibratorSetExternalControl failed because controller was not initialized");
+    NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("vibratorSetExternalControl failed because native service was not initialized");
         return;
     }
-    controller->setExternalControl(enabled);
+    service->controller()->setExternalControl(enabled);
 }
 
-static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
-    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
-    if (controller == nullptr) {
-        ALOGE("vibratorGetSupportedEffects failed because controller was not initialized");
+static jintArray vibratorGetSupportedEffects(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
+    NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("vibratorGetSupportedEffects failed because native service was not initialized");
         return nullptr;
     }
-    auto result = controller->getSupportedEffects();
+    auto result = service->controller()->getSupportedEffects();
     if (!result.isOk()) {
         return nullptr;
     }
@@ -181,14 +196,13 @@
     return effects;
 }
 
-static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */,
-                                                jlong controllerPtr) {
-    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
-    if (controller == nullptr) {
-        ALOGE("vibratorGetSupportedPrimitives failed because controller was not initialized");
+static jintArray vibratorGetSupportedPrimitives(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
+    NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("vibratorGetSupportedPrimitives failed because native service was not initialized");
         return nullptr;
     }
-    auto result = controller->getSupportedPrimitives();
+    auto result = service->controller()->getSupportedPrimitives();
     if (!result.isOk()) {
         return nullptr;
     }
@@ -199,26 +213,25 @@
     return primitives;
 }
 
-static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
-                                   jlong effect, jlong strength, jobject vibration) {
-    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
-    if (controller == nullptr) {
-        ALOGE("vibratorPerformEffect failed because controller was not initialized");
+static jlong vibratorPerformEffect(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong effect,
+                                   jlong strength, jlong vibrationId) {
+    NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("vibratorPerformEffect failed because native service was not initialized");
         return -1;
     }
     aidl::Effect effectType = static_cast<aidl::Effect>(effect);
     aidl::EffectStrength effectStrength = static_cast<aidl::EffectStrength>(strength);
-    jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration);
-    auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); };
-    auto result = controller->performEffect(effectType, effectStrength, callback);
+    auto callback = service->createCallback(vibrationId);
+    auto result = service->controller()->performEffect(effectType, effectStrength, callback);
     return result.isOk() ? result.value().count() : -1;
 }
 
-static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
-                                          jobjectArray composition, jobject vibration) {
-    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
-    if (controller == nullptr) {
-        ALOGE("vibratorPerformComposedEffect failed because controller was not initialized");
+static void vibratorPerformComposedEffect(JNIEnv* env, jclass /* clazz */, jlong servicePtr,
+                                          jobjectArray composition, jlong vibrationId) {
+    NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("vibratorPerformComposedEffect failed because native service was not initialized");
         return;
     }
     size_t size = env->GetArrayLength(composition);
@@ -227,54 +240,52 @@
         jobject element = env->GetObjectArrayElement(composition, i);
         effects.push_back(effectFromJavaPrimitive(env, element));
     }
-    jobject vibrationRef = vibration == nullptr ? vibration : MakeGlobalRefOrDie(env, vibration);
-    auto callback = [vibrationRef]() { callVibrationOnComplete(vibrationRef); };
-    controller->performComposedEffect(effects, callback);
+    auto callback = service->createCallback(vibrationId);
+    service->controller()->performComposedEffect(effects, callback);
 }
 
-static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong controllerPtr) {
-    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
-    if (controller == nullptr) {
-        ALOGE("vibratorGetCapabilities failed because controller was not initialized");
+static jlong vibratorGetCapabilities(JNIEnv* env, jclass /* clazz */, jlong servicePtr) {
+    NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("vibratorGetCapabilities failed because native service was not initialized");
         return 0;
     }
-    auto result = controller->getCapabilities();
+    auto result = service->controller()->getCapabilities();
     return result.isOk() ? static_cast<jlong>(result.value()) : 0;
 }
 
-static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong controllerPtr, jlong id,
+static void vibratorAlwaysOnEnable(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong id,
                                    jlong effect, jlong strength) {
-    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
-    if (controller == nullptr) {
-        ALOGE("vibratorAlwaysOnEnable failed because controller was not initialized");
+    NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("vibratorAlwaysOnEnable failed because native service was not initialized");
         return;
     }
-    controller->alwaysOnEnable(static_cast<int32_t>(id), static_cast<aidl::Effect>(effect),
-                               static_cast<aidl::EffectStrength>(strength));
+    service->controller()->alwaysOnEnable(static_cast<int32_t>(id),
+                                          static_cast<aidl::Effect>(effect),
+                                          static_cast<aidl::EffectStrength>(strength));
 }
 
-static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong controllerPtr,
-                                    jlong id) {
-    vibrator::HalController* controller = reinterpret_cast<vibrator::HalController*>(controllerPtr);
-    if (controller == nullptr) {
-        ALOGE("vibratorAlwaysOnDisable failed because controller was not initialized");
+static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong servicePtr, jlong id) {
+    NativeVibratorService* service = reinterpret_cast<NativeVibratorService*>(servicePtr);
+    if (service == nullptr) {
+        ALOGE("vibratorAlwaysOnDisable failed because native service was not initialized");
         return;
     }
-    controller->alwaysOnDisable(static_cast<int32_t>(id));
+    service->controller()->alwaysOnDisable(static_cast<int32_t>(id));
 }
 
 static const JNINativeMethod method_table[] = {
-        {"vibratorInit", "()J", (void*)vibratorInit},
+        {"vibratorInit", "(Lcom/android/server/VibratorService$OnCompleteListener;)J",
+         (void*)vibratorInit},
         {"vibratorGetFinalizer", "()J", (void*)vibratorGetFinalizer},
         {"vibratorExists", "(J)Z", (void*)vibratorExists},
-        {"vibratorOn", "(JJLcom/android/server/VibratorService$Vibration;)V", (void*)vibratorOn},
+        {"vibratorOn", "(JJJ)V", (void*)vibratorOn},
         {"vibratorOff", "(J)V", (void*)vibratorOff},
         {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude},
-        {"vibratorPerformEffect", "(JJJLcom/android/server/VibratorService$Vibration;)J",
-         (void*)vibratorPerformEffect},
+        {"vibratorPerformEffect", "(JJJJ)J", (void*)vibratorPerformEffect},
         {"vibratorPerformComposedEffect",
-         "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;Lcom/android/server/"
-         "VibratorService$Vibration;)V",
+         "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)V",
          (void*)vibratorPerformComposedEffect},
         {"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects},
         {"vibratorGetSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives},
@@ -284,18 +295,17 @@
         {"vibratorAlwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable},
 };
 
-int register_android_server_VibratorService(JavaVM* vm, JNIEnv* env) {
-    sJvm = vm;
-    sMethodIdOnComplete =
-            GetMethodIDOrDie(env,
-                             FindClassOrDie(env, "com/android/server/VibratorService$Vibration"),
-                             "onComplete", "()V");
+int register_android_server_VibratorService(JavaVM* jvm, JNIEnv* env) {
+    sJvm = jvm;
+    jclass listenerClass =
+            FindClassOrDie(env, "com/android/server/VibratorService$OnCompleteListener");
+    sMethodIdOnComplete = GetMethodIDOrDie(env, listenerClass, "onComplete", "(J)V");
 
     jclass primitiveClass =
             FindClassOrDie(env, "android/os/VibrationEffect$Composition$PrimitiveEffect");
-    gPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "id", "I");
-    gPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F");
-    gPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I");
+    sPrimitiveClassInfo.id = GetFieldIDOrDie(env, primitiveClass, "id", "I");
+    sPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F");
+    sPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I");
 
     return jniRegisterNativeMethods(env, "com/android/server/VibratorService", method_table,
                                     NELEM(method_table));
diff --git a/services/core/jni/com_android_server_security_VerityUtils.cpp b/services/core/jni/com_android_server_security_VerityUtils.cpp
index 0277f16..46e6f91 100644
--- a/services/core/jni/com_android_server_security_VerityUtils.cpp
+++ b/services/core/jni/com_android_server_security_VerityUtils.cpp
@@ -33,6 +33,8 @@
 
 #include <android-base/unique_fd.h>
 
+#include <type_traits>
+
 namespace android {
 
 namespace {
@@ -53,7 +55,7 @@
 
     fsverity_enable_arg arg = {};
     arg.version = 1;
-    arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
+    arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256; // hardcoded in measureFsverity below
     arg.block_size = 4096;
     arg.salt_size = 0;
     arg.salt_ptr = reinterpret_cast<uintptr_t>(nullptr);
@@ -85,9 +87,41 @@
     return (out.stx_attributes & STATX_ATTR_VERITY) != 0;
 }
 
+int measureFsverity(JNIEnv *env, jobject /* clazz */, jstring filePath, jbyteArray digest) {
+    static constexpr auto kDigestSha256 = 32;
+    using Storage = std::aligned_storage_t<sizeof(fsverity_digest) + kDigestSha256>;
+
+    Storage bytes;
+    fsverity_digest *data = reinterpret_cast<fsverity_digest *>(&bytes);
+    data->digest_size = kDigestSha256; // the only input/output parameter
+
+    ScopedUtfChars path(env, filePath);
+    ::android::base::unique_fd rfd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
+    if (rfd.get() < 0) {
+        return rfd.get();
+    }
+    if (auto err = ioctl(rfd.get(), FS_IOC_MEASURE_VERITY, data); err < 0) {
+        return err;
+    }
+
+    if (data->digest_algorithm != FS_VERITY_HASH_ALG_SHA256) {
+        return -EINVAL;
+    }
+
+    if (digest != nullptr && data->digest_size > 0) {
+        auto digestSize = env->GetArrayLength(digest);
+        if (data->digest_size > digestSize) {
+            return -E2BIG;
+        }
+        env->SetByteArrayRegion(digest, 0, data->digest_size, (const jbyte *)data->digest);
+    }
+
+    return 0;
+}
 const JNINativeMethod sMethods[] = {
         {"enableFsverityNative", "(Ljava/lang/String;[B)I", (void *)enableFsverity},
         {"statxForFsverityNative", "(Ljava/lang/String;)I", (void *)statxForFsverity},
+        {"measureFsverityNative", "(Ljava/lang/String;[B)I", (void *)measureFsverity},
 };
 
 }  // namespace
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f6d5872..277218d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -302,7 +302,6 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -322,7 +321,6 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Function;
 import java.util.function.Predicate;
 
@@ -575,13 +573,11 @@
 
     private final CertificateMonitor mCertificateMonitor;
     private final SecurityLogMonitor mSecurityLogMonitor;
+    private final RemoteBugreportManager mBugreportCollectionManager;
 
     @GuardedBy("getLockObject()")
     private NetworkLogger mNetworkLogger;
 
-    private final AtomicBoolean mRemoteBugreportServiceIsActive = new AtomicBoolean();
-    private final AtomicBoolean mRemoteBugreportSharingAccepted = new AtomicBoolean();
-
     private final SetupContentObserver mSetupContentObserver;
     private final DevicePolicyConstantsObserver mConstantsObserver;
 
@@ -633,44 +629,6 @@
     @VisibleForTesting
     final TransferOwnershipMetadataManager mTransferOwnershipMetadataManager;
 
-    private final Runnable mRemoteBugreportTimeoutRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if(mRemoteBugreportServiceIsActive.get()) {
-                onBugreportFailed();
-            }
-        }
-    };
-
-    /** Listens only if mHasFeature == true. */
-    private final BroadcastReceiver mRemoteBugreportFinishedReceiver = new BroadcastReceiver() {
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH.equals(intent.getAction())
-                    && mRemoteBugreportServiceIsActive.get()) {
-                onBugreportFinished(intent);
-            }
-        }
-    };
-
-    /** Listens only if mHasFeature == true. */
-    private final BroadcastReceiver mRemoteBugreportConsentReceiver = new BroadcastReceiver() {
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            mInjector.getNotificationManager().cancel(LOG_TAG,
-                    RemoteBugreportUtils.NOTIFICATION_ID);
-            if (DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED.equals(action)) {
-                onBugreportSharingAccepted();
-            } else if (DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED.equals(action)) {
-                onBugreportSharingDeclined();
-            }
-            mContext.unregisterReceiver(mRemoteBugreportConsentReceiver);
-        }
-    };
-
     public static final class Lifecycle extends SystemService {
         private BaseIDevicePolicyManager mService;
 
@@ -748,17 +706,9 @@
                 }
             }
             if (Intent.ACTION_BOOT_COMPLETED.equals(action)
-                    && userHandle == mOwners.getDeviceOwnerUserId()
-                    && getDeviceOwnerRemoteBugreportUri() != null) {
-                IntentFilter filterConsent = new IntentFilter();
-                filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED);
-                filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED);
-                mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
-                mInjector.getNotificationManager().notifyAsUser(LOG_TAG,
-                        RemoteBugreportUtils.NOTIFICATION_ID,
-                        RemoteBugreportUtils.buildNotification(mContext,
-                                DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED),
-                        UserHandle.ALL);
+                    && userHandle == mOwners.getDeviceOwnerUserId()) {
+                mBugreportCollectionManager.checkForPendingBugreportAfterBoot();
+
             }
             if (Intent.ACTION_BOOT_COMPLETED.equals(action)
                     || ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) {
@@ -1435,10 +1385,9 @@
         mCertificateMonitor = new CertificateMonitor(this, mInjector, mBackgroundHandler);
 
         mDeviceAdminServiceController = new DeviceAdminServiceController(this, mConstants);
-
         mOverlayPackagesProvider = new OverlayPackagesProvider(mContext);
-
         mTransferOwnershipMetadataManager = mInjector.newTransferOwnershipMetadataManager();
+        mBugreportCollectionManager = new RemoteBugreportManager(this, mInjector);
 
         if (!mHasFeature) {
             // Skip the rest of the initialization
@@ -1566,7 +1515,15 @@
     /**
      * Creates a new {@link CallerIdentity} object to represent the caller's identity.
      */
-    private CallerIdentity getCallerIdentity(String callerPackage) {
+    private CallerIdentity getCallerIdentity() {
+        final int callerUid = mInjector.binderGetCallingUid();
+        return new CallerIdentity(callerUid, null, null);
+    }
+
+    /**
+     * Creates a new {@link CallerIdentity} object to represent the caller's identity.
+     */
+    private CallerIdentity getCallerIdentity(@NonNull String callerPackage) {
         final int callerUid = mInjector.binderGetCallingUid();
 
         if (!isCallingFromPackage(callerPackage, callerUid)) {
@@ -2220,9 +2177,7 @@
         ActiveAdmin result = getActiveAdminWithPolicyForUidLocked(who, reqPolicy, callingUid);
         if (result != null) {
             return result;
-        } else if (permission != null
-                && (mContext.checkCallingPermission(permission)
-                        == PackageManager.PERMISSION_GRANTED)) {
+        } else if (permission != null && hasCallingPermission(permission)) {
             return null;
         }
 
@@ -2919,9 +2874,12 @@
 
     private void setActiveAdmin(ComponentName adminReceiver, boolean refreshing, int userHandle,
             Bundle onEnableData) {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.MANAGE_DEVICE_ADMINS, null);
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(
+                hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
 
         DevicePolicyData policy = getUserData(userHandle);
         DeviceAdminInfo info = findAdmin(adminReceiver, userHandle,
@@ -3061,7 +3019,11 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             return getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null;
         }
@@ -3072,7 +3034,11 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             DevicePolicyData policyData = getUserData(userHandle);
             return policyData.mRemovingAdmins.contains(adminReceiver);
@@ -3084,7 +3050,11 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity(adminReceiver);
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
             if (administrator == null) {
@@ -3100,8 +3070,11 @@
         if (!mHasFeature) {
             return Collections.EMPTY_LIST;
         }
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
 
-        enforceFullCrossUsersPermission(userHandle);
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             DevicePolicyData policy = getUserData(userHandle);
             final int N = policy.mAdminList.size();
@@ -3121,7 +3094,11 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             DevicePolicyData policy = getUserData(userHandle);
             final int N = policy.mAdminList.size();
@@ -3231,8 +3208,12 @@
         if (!mHasFeature) {
             return;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
         enforceUserUnlocked(userHandle);
+
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
             if (admin == null) {
@@ -3384,7 +3365,11 @@
         if (!mHasFeature) {
             return PASSWORD_QUALITY_UNSPECIFIED;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             int mode = PASSWORD_QUALITY_UNSPECIFIED;
 
@@ -3596,7 +3581,11 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return 0L;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             long timeout = 0L;
 
@@ -3623,11 +3612,11 @@
     @Override
     public boolean addCrossProfileWidgetProvider(ComponentName admin, String packageName) {
         final CallerIdentity identity = getCallerIdentity(admin);
-        Preconditions.checkCallAuthorization(isProfileOwner(identity));
+        Preconditions.checkCallAuthorization(isProfileOwner(identity) || isDeviceOwner(identity));
         List<String> changedProviders = null;
 
         synchronized (getLockObject()) {
-            ActiveAdmin activeAdmin = getProfileOwnerOfCallerLocked(identity);
+            ActiveAdmin activeAdmin = getProfileOwnerOrDeviceOwnerLocked(identity);
             if (activeAdmin.crossProfileWidgetProviders == null) {
                 activeAdmin.crossProfileWidgetProviders = new ArrayList<>();
             }
@@ -3656,11 +3645,11 @@
     @Override
     public boolean removeCrossProfileWidgetProvider(ComponentName admin, String packageName) {
         final CallerIdentity identity = getCallerIdentity(admin);
-        Preconditions.checkCallAuthorization(isProfileOwner(identity));
+        Preconditions.checkCallAuthorization(isProfileOwner(identity) || isDeviceOwner(identity));
         List<String> changedProviders = null;
 
         synchronized (getLockObject()) {
-            ActiveAdmin activeAdmin = getProfileOwnerOfCallerLocked(identity);
+            ActiveAdmin activeAdmin = getProfileOwnerOrDeviceOwnerLocked(identity);
             if (activeAdmin.crossProfileWidgetProviders == null
                     || activeAdmin.crossProfileWidgetProviders.isEmpty()) {
                 return false;
@@ -3689,10 +3678,10 @@
     @Override
     public List<String> getCrossProfileWidgetProviders(ComponentName admin) {
         final CallerIdentity identity = getCallerIdentity(admin);
-        Preconditions.checkCallAuthorization(isProfileOwner(identity));
+        Preconditions.checkCallAuthorization(isProfileOwner(identity) || isDeviceOwner(identity));
 
         synchronized (getLockObject()) {
-            ActiveAdmin activeAdmin = getProfileOwnerOfCallerLocked(identity);
+            ActiveAdmin activeAdmin = getProfileOwnerOrDeviceOwnerLocked(identity);
             if (activeAdmin.crossProfileWidgetProviders == null
                     || activeAdmin.crossProfileWidgetProviders.isEmpty()) {
                 return null;
@@ -3735,7 +3724,11 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return 0L;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             return getPasswordExpirationLocked(who, userHandle, parent);
         }
@@ -3941,7 +3934,11 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             if (who != null) {
                 final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
@@ -3981,7 +3978,11 @@
         if (!mHasFeature) {
             new PasswordMetrics(CREDENTIAL_TYPE_NONE);
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         ArrayList<PasswordMetrics> adminMetrics = new ArrayList<>();
         synchronized (getLockObject()) {
             List<ActiveAdmin> admins =
@@ -3998,7 +3999,10 @@
         if (!mHasFeature) {
             return true;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
         enforceUserUnlocked(userHandle, parent);
 
         synchronized (getLockObject()) {
@@ -4030,7 +4034,10 @@
         if (!mHasFeature) {
             return true;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
         enforceManagedProfile(userHandle, "call APIs refering to the parent profile");
 
         synchronized (getLockObject()) {
@@ -4049,7 +4056,10 @@
         if (!mHasFeature) {
             return true;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
         enforceNotManagedProfile(userHandle, "check password sufficiency");
         enforceUserUnlocked(userHandle);
 
@@ -4137,12 +4147,15 @@
         if (!mLockPatternUtils.hasSecureLockScreen()) {
             return 0;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
-            if (!isCallerWithSystemUid()) {
+            if (!isSystemUid(identity)) {
                 // This API can be called by an active device admin or by keyguard code.
-                if (mContext.checkCallingPermission(permission.ACCESS_KEYGUARD_SECURE_STORAGE)
-                        != PackageManager.PERMISSION_GRANTED) {
+                if (!hasCallingPermission(permission.ACCESS_KEYGUARD_SECURE_STORAGE)) {
                     getActiveAdminForCallerLocked(
                             null, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, parent);
                 }
@@ -4185,7 +4198,11 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return 0;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             ActiveAdmin admin = (who != null)
                     ? getActiveAdminUncheckedLocked(who, userHandle, parent)
@@ -4199,7 +4216,11 @@
         if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) {
             return UserHandle.USER_NULL;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             ActiveAdmin admin = getAdminWithMinimumFailedPasswordsForWipeLocked(
                     userHandle, parent);
@@ -4270,8 +4291,7 @@
 
         // As of R, only privlleged caller holding RESET_PASSWORD can call resetPassword() to
         // set password to an unsecured user.
-        if (mContext.checkCallingPermission(permission.RESET_PASSWORD)
-                == PackageManager.PERMISSION_GRANTED) {
+        if (hasCallingPermission(permission.RESET_PASSWORD)) {
             return setPasswordPrivileged(password, flags, callingUid);
         }
 
@@ -4471,7 +4491,11 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             if (who != null) {
                 final ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
@@ -4544,12 +4568,16 @@
         if (!mHasFeature) {
             return DevicePolicyManager.DEFAULT_STRONG_AUTH_TIMEOUT_MS;
         }
+        Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userId));
+
         if (!mLockPatternUtils.hasSecureLockScreen()) {
             // No strong auth timeout on devices not supporting the
             // {@link PackageManager#FEATURE_SECURE_LOCK_SCREEN} feature
             return 0;
         }
-        enforceFullCrossUsersPermission(userId);
         synchronized (getLockObject()) {
             if (who != null) {
                 ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userId, parent);
@@ -4583,8 +4611,7 @@
 
     @Override
     public void lockNow(int flags, boolean parent) {
-        if (!mHasFeature && mContext.checkCallingPermission(android.Manifest.permission.LOCK_DEVICE)
-                != PackageManager.PERMISSION_GRANTED) {
+        if (!mHasFeature && !hasCallingPermission(permission.LOCK_DEVICE)) {
             return;
         }
 
@@ -4676,8 +4703,7 @@
     }
 
     private void enforceNetworkStackOrProfileOrDeviceOwner(ComponentName who) {
-        if (mContext.checkCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)
-                == PackageManager.PERMISSION_GRANTED) {
+        if (hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)) {
             return;
         }
         enforceProfileOrDeviceOwner(who);
@@ -5756,8 +5782,9 @@
         if (!mHasFeature) {
             return;
         }
-
-        enforceFullCrossUsersPermission(mInjector.userHandleGetCallingUserId());
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(isSystemUid(identity) || isRootUid(identity)
+                || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL));
 
         final ActiveAdmin admin;
         synchronized (getLockObject()) {
@@ -5933,8 +5960,7 @@
         synchronized (getLockObject()) {
             if (who == null) {
                 if ((frpManagementAgentUid != mInjector.binderGetCallingUid())
-                        && (mContext.checkCallingPermission(permission.MASTER_CLEAR)
-                        != PackageManager.PERMISSION_GRANTED)) {
+                        && !hasCallingPermission(permission.MASTER_CLEAR)) {
                     throw new SecurityException(
                             "Must be called by the FRP management agent on device");
                 }
@@ -5972,9 +5998,13 @@
         if (!mHasFeature) {
             return;
         }
-        enforceFullCrossUsersPermission(userHandle);
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = comp != null
+                ? getCallerIdentity(comp)
+                : getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
 
         synchronized (getLockObject()) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(comp, userHandle);
@@ -6051,13 +6081,15 @@
 
     @Override
     public void reportFailedPasswordAttempt(int userHandle) {
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
         if (!isSeparateProfileChallengeEnabled(userHandle)) {
             enforceNotManagedProfile(userHandle,
                     "report failed password attempt if separate profile challenge is not in place");
         }
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
 
         boolean wipeData = false;
         ActiveAdmin strictestAdmin = null;
@@ -6130,9 +6162,11 @@
 
     @Override
     public void reportSuccessfulPasswordAttempt(int userHandle) {
-        enforceFullCrossUsersPermission(userHandle);
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
 
         synchronized (getLockObject()) {
             DevicePolicyData policy = getUserData(userHandle);
@@ -6158,9 +6192,12 @@
 
     @Override
     public void reportFailedBiometricAttempt(int userHandle) {
-        enforceFullCrossUsersPermission(userHandle);
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
+
         if (mInjector.securityLogIsLoggingEnabled()) {
             SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 0,
                     /*method strength*/ 0);
@@ -6169,9 +6206,12 @@
 
     @Override
     public void reportSuccessfulBiometricAttempt(int userHandle) {
-        enforceFullCrossUsersPermission(userHandle);
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
+
         if (mInjector.securityLogIsLoggingEnabled()) {
             SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, /*result*/ 1,
                     /*method strength*/ 0);
@@ -6180,9 +6220,11 @@
 
     @Override
     public void reportKeyguardDismissed(int userHandle) {
-        enforceFullCrossUsersPermission(userHandle);
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
 
         if (mInjector.securityLogIsLoggingEnabled()) {
             SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_DISMISSED);
@@ -6191,9 +6233,11 @@
 
     @Override
     public void reportKeyguardSecured(int userHandle) {
-        enforceFullCrossUsersPermission(userHandle);
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.BIND_DEVICE_ADMIN, null);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+        Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN));
 
         if (mInjector.securityLogIsLoggingEnabled()) {
             SecurityLog.writeEvent(SecurityLog.TAG_KEYGUARD_SECURED);
@@ -6255,7 +6299,11 @@
         if (!mHasFeature) {
             return null;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             DevicePolicyData policy = getUserData(UserHandle.USER_SYSTEM);
             // Scan through active admins and find if anyone has already
@@ -6389,7 +6437,13 @@
         if (!mHasFeature) {
             return false;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = who != null
+                ? getCallerIdentity(who)
+                : getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             // Check for permissions if a particular caller is specified
             if (who != null) {
@@ -6419,7 +6473,12 @@
         if (!mHasFeature) {
             // Ok to return current status.
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = callerPackage != null
+                ? getCallerIdentity(callerPackage)
+                : getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
 
         // It's not critical here, but let's make sure the package name is correct, in case
         // we start using it for different purposes.
@@ -6757,45 +6816,24 @@
         Preconditions.checkCallAuthorization(isDeviceOwner(identity));
         ensureAllUsersAffiliated();
 
-        if (mRemoteBugreportServiceIsActive.get()
-                || (getDeviceOwnerRemoteBugreportUri() != null)) {
-            Slog.d(LOG_TAG, "Remote bugreport wasn't started because there's already one running.");
-            return false;
-        }
-
-        final long currentTime = System.currentTimeMillis();
-        synchronized (getLockObject()) {
-            DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
-            if (currentTime > policyData.mLastBugReportRequestTime) {
-                policyData.mLastBugReportRequestTime = currentTime;
-                saveSettingsLocked(UserHandle.USER_SYSTEM);
-            }
-        }
-
-        final long callingIdentity = mInjector.binderClearCallingIdentity();
-        try {
-            mInjector.getIActivityManager().requestRemoteBugReport();
-
-            mRemoteBugreportServiceIsActive.set(true);
-            mRemoteBugreportSharingAccepted.set(false);
-            registerRemoteBugreportReceivers();
-            mInjector.getNotificationManager().notifyAsUser(LOG_TAG,
-                    RemoteBugreportUtils.NOTIFICATION_ID,
-                    RemoteBugreportUtils.buildNotification(mContext,
-                            DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED), UserHandle.ALL);
-            mHandler.postDelayed(mRemoteBugreportTimeoutRunnable,
-                    RemoteBugreportUtils.REMOTE_BUGREPORT_TIMEOUT_MILLIS);
+        if (mBugreportCollectionManager.requestBugreport()) {
             DevicePolicyEventLogger
                     .createEvent(DevicePolicyEnums.REQUEST_BUGREPORT)
                     .setAdmin(who)
                     .write();
+
+            final long currentTime = System.currentTimeMillis();
+            synchronized (getLockObject()) {
+                DevicePolicyData policyData = getUserData(UserHandle.USER_SYSTEM);
+                if (currentTime > policyData.mLastBugReportRequestTime) {
+                    policyData.mLastBugReportRequestTime = currentTime;
+                    saveSettingsLocked(UserHandle.USER_SYSTEM);
+                }
+            }
+
             return true;
-        } catch (RemoteException re) {
-            // should never happen
-            Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re);
+        } else {
             return false;
-        } finally {
-            mInjector.binderRestoreCallingIdentity(callingIdentity);
         }
     }
 
@@ -6839,146 +6877,36 @@
         mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
     }
 
-    private String getDeviceOwnerRemoteBugreportUri() {
+    void sendBugreportToDeviceOwner(Uri bugreportUri, String bugreportHash) {
         synchronized (getLockObject()) {
-            return mOwners.getDeviceOwnerRemoteBugreportUri();
+            final Intent intent = new Intent(DeviceAdminReceiver.ACTION_BUGREPORT_SHARE);
+            intent.setComponent(mOwners.getDeviceOwnerComponent());
+            intent.setDataAndType(bugreportUri, RemoteBugreportManager.BUGREPORT_MIMETYPE);
+            intent.putExtra(DeviceAdminReceiver.EXTRA_BUGREPORT_HASH, bugreportHash);
+            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+            final UriGrantsManagerInternal ugm = LocalServices
+                    .getService(UriGrantsManagerInternal.class);
+            final NeededUriGrants needed = ugm.checkGrantUriPermissionFromIntent(intent,
+                    Process.SHELL_UID, mOwners.getDeviceOwnerComponent().getPackageName(),
+                    mOwners.getDeviceOwnerUserId());
+            ugm.grantUriPermissionUncheckedFromIntent(needed, null);
+
+            mContext.sendBroadcastAsUser(intent, UserHandle.of(mOwners.getDeviceOwnerUserId()));
         }
     }
 
-    private void setDeviceOwnerRemoteBugreportUriAndHash(String bugreportUri,
-            String bugreportHash) {
+    void setDeviceOwnerRemoteBugreportUriAndHash(String bugreportUri, String bugreportHash) {
         synchronized (getLockObject()) {
             mOwners.setDeviceOwnerRemoteBugreportUriAndHash(bugreportUri, bugreportHash);
         }
     }
 
-    private void registerRemoteBugreportReceivers() {
-        try {
-            IntentFilter filterFinished = new IntentFilter(
-                    DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH,
-                    RemoteBugreportUtils.BUGREPORT_MIMETYPE);
-            mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished);
-        } catch (IntentFilter.MalformedMimeTypeException e) {
-            // should never happen, as setting a constant
-            Slog.w(LOG_TAG, "Failed to set type " + RemoteBugreportUtils.BUGREPORT_MIMETYPE, e);
-        }
-        IntentFilter filterConsent = new IntentFilter();
-        filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED);
-        filterConsent.addAction(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED);
-        mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
-    }
-
-    private void onBugreportFinished(Intent intent) {
-        mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable);
-        mRemoteBugreportServiceIsActive.set(false);
-        Uri bugreportUri = intent.getData();
-        String bugreportUriString = null;
-        if (bugreportUri != null) {
-            bugreportUriString = bugreportUri.toString();
-        }
-        String bugreportHash = intent.getStringExtra(
-                DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH);
-        if (mRemoteBugreportSharingAccepted.get()) {
-            shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash);
-            mInjector.getNotificationManager().cancel(LOG_TAG,
-                    RemoteBugreportUtils.NOTIFICATION_ID);
-        } else {
-            setDeviceOwnerRemoteBugreportUriAndHash(bugreportUriString, bugreportHash);
-            mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
-                    RemoteBugreportUtils.buildNotification(mContext,
-                            DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED),
-                            UserHandle.ALL);
-        }
-        mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
-    }
-
-    private void onBugreportFailed() {
-        mRemoteBugreportServiceIsActive.set(false);
-        mInjector.systemPropertiesSet(RemoteBugreportUtils.CTL_STOP,
-                RemoteBugreportUtils.REMOTE_BUGREPORT_SERVICE);
-        mRemoteBugreportSharingAccepted.set(false);
-        setDeviceOwnerRemoteBugreportUriAndHash(null, null);
-        mInjector.getNotificationManager().cancel(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID);
-        Bundle extras = new Bundle();
-        extras.putInt(DeviceAdminReceiver.EXTRA_BUGREPORT_FAILURE_REASON,
-                DeviceAdminReceiver.BUGREPORT_FAILURE_FAILED_COMPLETING);
-        sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_FAILED, extras);
-        mContext.unregisterReceiver(mRemoteBugreportConsentReceiver);
-        mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
-    }
-
-    private void onBugreportSharingAccepted() {
-        mRemoteBugreportSharingAccepted.set(true);
-        String bugreportUriString = null;
-        String bugreportHash = null;
+    Pair<String, String> getDeviceOwnerRemoteBugreportUriAndHash() {
         synchronized (getLockObject()) {
-            bugreportUriString = getDeviceOwnerRemoteBugreportUri();
-            bugreportHash = mOwners.getDeviceOwnerRemoteBugreportHash();
-        }
-        if (bugreportUriString != null) {
-            shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash);
-        } else if (mRemoteBugreportServiceIsActive.get()) {
-            mInjector.getNotificationManager().notifyAsUser(LOG_TAG, RemoteBugreportUtils.NOTIFICATION_ID,
-                    RemoteBugreportUtils.buildNotification(mContext,
-                            DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED),
-                            UserHandle.ALL);
-        }
-    }
-
-    private void onBugreportSharingDeclined() {
-        if (mRemoteBugreportServiceIsActive.get()) {
-            mInjector.systemPropertiesSet(RemoteBugreportUtils.CTL_STOP,
-                    RemoteBugreportUtils.REMOTE_BUGREPORT_SERVICE);
-            mRemoteBugreportServiceIsActive.set(false);
-            mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable);
-            mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
-        }
-        mRemoteBugreportSharingAccepted.set(false);
-        setDeviceOwnerRemoteBugreportUriAndHash(null, null);
-        sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_SHARING_DECLINED, null);
-    }
-
-    private void shareBugreportWithDeviceOwnerIfExists(String bugreportUriString,
-            String bugreportHash) {
-        ParcelFileDescriptor pfd = null;
-        try {
-            if (bugreportUriString == null) {
-                throw new FileNotFoundException();
-            }
-            Uri bugreportUri = Uri.parse(bugreportUriString);
-            pfd = mContext.getContentResolver().openFileDescriptor(bugreportUri, "r");
-
-            synchronized (getLockObject()) {
-                Intent intent = new Intent(DeviceAdminReceiver.ACTION_BUGREPORT_SHARE);
-                intent.setComponent(mOwners.getDeviceOwnerComponent());
-                intent.setDataAndType(bugreportUri, RemoteBugreportUtils.BUGREPORT_MIMETYPE);
-                intent.putExtra(DeviceAdminReceiver.EXTRA_BUGREPORT_HASH, bugreportHash);
-                intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-
-                final UriGrantsManagerInternal ugm = LocalServices
-                        .getService(UriGrantsManagerInternal.class);
-                final NeededUriGrants needed = ugm.checkGrantUriPermissionFromIntent(intent,
-                        Process.SHELL_UID, mOwners.getDeviceOwnerComponent().getPackageName(),
-                        mOwners.getDeviceOwnerUserId());
-                ugm.grantUriPermissionUncheckedFromIntent(needed, null);
-
-                mContext.sendBroadcastAsUser(intent, UserHandle.of(mOwners.getDeviceOwnerUserId()));
-            }
-        } catch (FileNotFoundException e) {
-            Bundle extras = new Bundle();
-            extras.putInt(DeviceAdminReceiver.EXTRA_BUGREPORT_FAILURE_REASON,
-                    DeviceAdminReceiver.BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE);
-            sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_FAILED, extras);
-        } finally {
-            try {
-                if (pfd != null) {
-                    pfd.close();
-                }
-            } catch (IOException ex) {
-                // Ignore
-            }
-            mRemoteBugreportSharingAccepted.set(false);
-            setDeviceOwnerRemoteBugreportUriAndHash(null, null);
+            final String uri = mOwners.getDeviceOwnerRemoteBugreportUri();
+            return uri == null ? null
+                    : new Pair<>(uri, mOwners.getDeviceOwnerRemoteBugreportHash());
         }
     }
 
@@ -7111,7 +7039,11 @@
         if (!mHasFeature) {
             return 0;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         final long ident = mInjector.binderClearCallingIdentity();
         try {
             synchronized (getLockObject()) {
@@ -7871,7 +7803,10 @@
 
     @Override
     public ComponentName getProfileOwnerAsUser(int userHandle) {
-        enforceCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userHandle));
 
         return getProfileOwner(userHandle);
     }
@@ -8228,56 +8163,31 @@
         }
     }
 
-    private void enforceAcrossUsersPermissions() {
-        final int callingUid = mInjector.binderGetCallingUid();
+    private boolean hasCallingPermission(String permission) {
+        return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private boolean hasCallingOrSelfPermission(String permission) {
+        return mContext.checkCallingOrSelfPermission(permission)
+                == PackageManager.PERMISSION_GRANTED;
+    }
+
+    private boolean hasPermissionForPreflight(CallerIdentity identity, String permission) {
         final int callingPid = mInjector.binderGetCallingPid();
         final String packageName = mContext.getPackageName();
 
-        if (isCallerWithSystemUid() || callingUid == Process.ROOT_UID) {
-            return;
-        }
-        if (PermissionChecker.checkPermissionForPreflight(
-                mContext, permission.INTERACT_ACROSS_PROFILES, callingPid, callingUid,
-                packageName) == PermissionChecker.PERMISSION_GRANTED) {
-            return;
-        }
-        if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_USERS)
-                == PackageManager.PERMISSION_GRANTED) {
-            return;
-        }
-        if (mContext.checkCallingPermission(permission.INTERACT_ACROSS_USERS_FULL)
-                == PackageManager.PERMISSION_GRANTED) {
-            return;
-        }
-        throw new SecurityException("Calling user does not have INTERACT_ACROSS_PROFILES or"
-                + "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL permissions");
+        return PermissionChecker.checkPermissionForPreflight(mContext, permission, callingPid,
+                identity.getUid(), packageName) == PermissionChecker.PERMISSION_GRANTED;
     }
 
-    private void enforceFullCrossUsersPermission(int userHandle) {
-        enforceSystemUserOrPermissionIfCrossUser(userHandle,
-                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+    private boolean hasFullCrossUsersPermission(CallerIdentity identity, int userHandle) {
+        return (userHandle == identity.getUserId()) || isSystemUid(identity) || isRootUid(identity)
+                || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS_FULL);
     }
 
-    private void enforceCrossUsersPermission(int userHandle) {
-        enforceSystemUserOrPermissionIfCrossUser(userHandle,
-                android.Manifest.permission.INTERACT_ACROSS_USERS);
-    }
-
-    private void enforceSystemUserOrPermission(String permission) {
-        if (!(isCallerWithSystemUid() || mInjector.binderGetCallingUid() == Process.ROOT_UID)) {
-            mContext.enforceCallingOrSelfPermission(permission,
-                    "Must be system or have " + permission + " permission");
-        }
-    }
-
-    private void enforceSystemUserOrPermissionIfCrossUser(int userHandle, String permission) {
-        if (userHandle < 0) {
-            throw new IllegalArgumentException("Invalid userId " + userHandle);
-        }
-        if (userHandle == mInjector.userHandleGetCallingUserId()) {
-            return;
-        }
-        enforceSystemUserOrPermission(permission);
+    private boolean hasCrossUsersPermission(CallerIdentity identity, int userHandle) {
+        return (userHandle == identity.getUserId()) || isSystemUid(identity) || isRootUid(identity)
+                || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS);
     }
 
     private void enforceManagedProfile(int userId, String message) {
@@ -8337,19 +8247,18 @@
         throw new SecurityException("No active admin found");
     }
 
-    private void enforceProfileOwnerOrFullCrossUsersPermission(int userId) {
-        if (userId == mInjector.userHandleGetCallingUserId()) {
+    private void enforceProfileOwnerOrFullCrossUsersPermission(CallerIdentity identity,
+            int userId) {
+        if (userId == identity.getUserId()) {
             synchronized (getLockObject()) {
                 if (getActiveAdminWithPolicyForUidLocked(null,
-                        DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, mInjector.binderGetCallingUid())
-                                != null) {
+                        DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, identity.getUid()) != null) {
                     // Device Owner/Profile Owner may access the user it runs on.
                     return;
                 }
             }
         }
-        // Otherwise, INTERACT_ACROSS_USERS_FULL permission, system UID or root UID is required.
-        enforceSystemUserOrPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userId));
     }
 
     private boolean canUserUseLockTaskLocked(int userId) {
@@ -8403,6 +8312,18 @@
         return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID);
     }
 
+    private boolean isSystemUid(CallerIdentity identity) {
+        return UserHandle.isSameApp(identity.getUid(), Process.SYSTEM_UID);
+    }
+
+    private boolean isRootUid(CallerIdentity identity) {
+        return UserHandle.isSameApp(identity.getUid(), Process.ROOT_UID);
+    }
+
+    private boolean isShellUid(CallerIdentity identity) {
+        return UserHandle.isSameApp(identity.getUid(), Process.SHELL_UID);
+    }
+
     protected int getProfileParentId(int userHandle) {
         return mInjector.binderWithCleanCallingIdentity(() -> {
             UserInfo parentUser = mUserManager.getProfileParent(userHandle);
@@ -8657,7 +8578,12 @@
             return null;
         }
         Objects.requireNonNull(agent, "agent null");
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = admin != null
+                ? getCallerIdentity(admin)
+                : getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
 
         synchronized (getLockObject()) {
             final String componentName = agent.flattenToString();
@@ -9135,7 +9061,7 @@
         }
 
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity);
+            ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
             admin.permittedNotificationListeners = packageList;
             saveSettingsLocked(identity.getUserId());
         }
@@ -10014,10 +9940,14 @@
 
     @Override
     public String[] getAccountTypesWithManagementDisabledAsUser(int userId, boolean parent) {
-        enforceFullCrossUsersPermission(userId);
         if (!mHasFeature) {
             return null;
         }
+        Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userId));
+
         synchronized (getLockObject()) {
             final ArraySet<String> resultSet = new ArraySet<>();
 
@@ -10110,10 +10040,10 @@
         }
         Objects.requireNonNull(who, "ComponentName is null");
         final CallerIdentity identity = getCallerIdentity(who);
-        Preconditions.checkCallAuthorization(isProfileOwner(identity));
+        Preconditions.checkCallAuthorization(isProfileOwner(identity) || isDeviceOwner(identity));
 
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity);
+            ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
             if (admin.disableCallerId != disabled) {
                 admin.disableCallerId = disabled;
                 saveSettingsLocked(identity.getUserId());
@@ -10133,17 +10063,21 @@
         }
         Objects.requireNonNull(who, "ComponentName is null");
         final CallerIdentity identity = getCallerIdentity(who);
-        Preconditions.checkCallAuthorization(isProfileOwner(identity));
+        Preconditions.checkCallAuthorization(isProfileOwner(identity) || isDeviceOwner(identity));
 
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity);
+            ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
             return admin.disableCallerId;
         }
     }
 
     @Override
     public boolean getCrossProfileCallerIdDisabledForUser(int userId) {
-        enforceCrossUsersPermission(userId);
+        Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userId));
+
         synchronized (getLockObject()) {
             ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
             return (admin != null) ? admin.disableCallerId : false;
@@ -10157,10 +10091,10 @@
         }
         Objects.requireNonNull(who, "ComponentName is null");
         final CallerIdentity identity = getCallerIdentity(who);
-        Preconditions.checkCallAuthorization(isProfileOwner(identity));
+        Preconditions.checkCallAuthorization(isProfileOwner(identity) || isDeviceOwner(identity));
 
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity);
+            ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
             if (admin.disableContactsSearch != disabled) {
                 admin.disableContactsSearch = disabled;
                 saveSettingsLocked(identity.getUserId());
@@ -10180,17 +10114,21 @@
         }
         Objects.requireNonNull(who, "ComponentName is null");
         final CallerIdentity identity = getCallerIdentity(who);
-        Preconditions.checkCallAuthorization(isProfileOwner(identity));
+        Preconditions.checkCallAuthorization(isProfileOwner(identity) || isDeviceOwner(identity));
 
         synchronized (getLockObject()) {
-            ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity);
+            ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
             return admin.disableContactsSearch;
         }
     }
 
     @Override
     public boolean getCrossProfileContactsSearchDisabledForUser(int userId) {
-        enforceCrossUsersPermission(userId);
+        Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userId));
+
         synchronized (getLockObject()) {
             ActiveAdmin admin = getProfileOwnerAdminLocked(userId);
             return (admin != null) ? admin.disableContactsSearch : false;
@@ -12114,14 +12052,6 @@
     }
 
     @Override
-    public boolean isSystemOnlyUser(ComponentName admin) {
-        Objects.requireNonNull(admin, "ComponentName is null");
-        final CallerIdentity identity = getCallerIdentity(admin);
-        Preconditions.checkCallAuthorization(isDeviceOwner(identity));
-        return UserManager.isSplitSystemUser() && identity.getUserId() == UserHandle.USER_SYSTEM;
-    }
-
-    @Override
     public void reboot(ComponentName admin) {
         Objects.requireNonNull(admin, "ComponentName is null");
         final CallerIdentity identity = getCallerIdentity(admin);
@@ -12262,7 +12192,11 @@
         if (!mHasFeature) {
             return;
         }
-        enforceFullCrossUsersPermission(userId);
+        Preconditions.checkArgumentNonnegative(userId, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userId));
+
         enforceManageUsers();
         enforceManagedProfile(userId, "set organization color");
         synchronized (getLockObject()) {
@@ -12291,7 +12225,11 @@
         if (!mHasFeature) {
             return ActiveAdmin.DEF_ORGANIZATION_COLOR;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         enforceManagedProfile(userHandle, "get organization color");
         synchronized (getLockObject()) {
             ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle);
@@ -12350,7 +12288,11 @@
         if (!mHasFeature) {
             return null;
         }
-        enforceFullCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(identity, userHandle));
+
         enforceManagedProfile(userHandle, "get organization name");
         synchronized (getLockObject()) {
             ActiveAdmin profileOwner = getProfileOwnerAdminLocked(userHandle);
@@ -12445,12 +12387,6 @@
         return false;
     }
 
-    private boolean hasMarkProfileOwnerOnOrganizationOwnedDevicePermission() {
-        return mContext.checkCallingPermission(
-                permission.MARK_DEVICE_ORGANIZATION_OWNED)
-                == PackageManager.PERMISSION_GRANTED;
-    }
-
     @Override
     public void markProfileOwnerOnOrganizationOwnedDevice(ComponentName who, int userId) {
         // As the caller is the system, it must specify the component name of the profile owner
@@ -12463,7 +12399,7 @@
 
         // Only adb or system apps with the right permission can mark a profile owner on
         // organization-owned device.
-        if (!(isAdb() || hasMarkProfileOwnerOnOrganizationOwnedDevicePermission())) {
+        if (!(isAdb() || hasCallingPermission(permission.MARK_DEVICE_ORGANIZATION_OWNED))) {
             throw new SecurityException(
                     "Only the system can mark a profile owner of organization-owned device.");
         }
@@ -13591,7 +13527,8 @@
     @Override
     public StringParceledListSlice getOwnerInstalledCaCerts(@NonNull UserHandle user) {
         final int userId = user.getIdentifier();
-        enforceProfileOwnerOrFullCrossUsersPermission(userId);
+        final CallerIdentity identity = getCallerIdentity();
+        enforceProfileOwnerOrFullCrossUsersPermission(identity, userId);
         synchronized (getLockObject()) {
             return new StringParceledListSlice(
                     new ArrayList<>(getUserData(userId).mOwnerInstalledCaCerts));
@@ -14211,7 +14148,7 @@
         final CallerIdentity identity = getCallerIdentity(who);
 
         synchronized (getLockObject()) {
-            final ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity);
+            final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
             admin.mCrossProfileCalendarPackages = packageNames;
             saveSettingsLocked(identity.getUserId());
         }
@@ -14232,7 +14169,7 @@
         final CallerIdentity identity = getCallerIdentity(who);
 
         synchronized (getLockObject()) {
-            final ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity);
+            final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
             return admin.mCrossProfileCalendarPackages;
         }
     }
@@ -14244,8 +14181,11 @@
             return false;
         }
         Preconditions.checkStringNotEmpty(packageName, "Package name is null or empty");
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
 
-        enforceCrossUsersPermission(userHandle);
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             if (mInjector.settingsSecureGetIntForUser(
                     Settings.Secure.CROSS_PROFILE_CALENDAR_ENABLED, 0, userHandle) == 0) {
@@ -14267,7 +14207,11 @@
         if (!mHasFeature) {
             return Collections.emptyList();
         }
-        enforceCrossUsersPermission(userHandle);
+        Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId");
+
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(hasCrossUsersPermission(identity, userHandle));
+
         synchronized (getLockObject()) {
             final ActiveAdmin admin = getProfileOwnerAdminLocked(userHandle);
             if (admin != null) {
@@ -14288,7 +14232,7 @@
 
         final List<String> previousCrossProfilePackages;
         synchronized (getLockObject()) {
-            final ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity);
+            final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
             previousCrossProfilePackages = admin.mCrossProfilePackages;
             if (packageNames.equals(previousCrossProfilePackages)) {
                 return;
@@ -14320,7 +14264,7 @@
         final CallerIdentity identity = getCallerIdentity(who);
 
         synchronized (getLockObject()) {
-            final ActiveAdmin admin = getProfileOwnerOfCallerLocked(identity);
+            final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(identity);
             return admin.mCrossProfilePackages;
         }
     }
@@ -14330,7 +14274,12 @@
         if (!mHasFeature) {
             return Collections.emptyList();
         }
-        enforceAcrossUsersPermissions();
+        final CallerIdentity identity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(
+                isSystemUid(identity) || isRootUid(identity) || hasCallingPermission(
+                        permission.INTERACT_ACROSS_USERS) || hasCallingPermission(
+                        permission.INTERACT_ACROSS_USERS_FULL) || hasPermissionForPreflight(
+                        identity, permission.INTERACT_ACROSS_PROFILES));
 
         synchronized (getLockObject()) {
             final List<ActiveAdmin> admins = getProfileOwnerAdminsForCurrentProfileGroup();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
new file mode 100644
index 0000000..46c9aab
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java
@@ -0,0 +1,324 @@
+/*
+ * 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.devicepolicy;
+
+import static android.app.admin.DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED;
+import static android.app.admin.DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED;
+import static android.app.admin.DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH;
+import static android.app.admin.DevicePolicyManager.EXTRA_BUGREPORT_NOTIFICATION_TYPE;
+import static android.app.admin.DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH;
+import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED;
+import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED;
+import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED;
+
+import android.annotation.IntDef;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.admin.DeviceAdminReceiver;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.format.DateUtils;
+import android.util.Pair;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
+
+import java.io.FileNotFoundException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Class managing bugreport collection upon device owner's request.
+ */
+public class RemoteBugreportManager {
+    private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG;
+
+    static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
+
+    private static final long REMOTE_BUGREPORT_TIMEOUT_MILLIS = 10 * DateUtils.MINUTE_IN_MILLIS;
+    private static final String CTL_STOP = "ctl.stop";
+    private static final String REMOTE_BUGREPORT_SERVICE = "bugreportd";
+    private static final int NOTIFICATION_ID = SystemMessage.NOTE_REMOTE_BUGREPORT;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            NOTIFICATION_BUGREPORT_STARTED,
+            NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED,
+            NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED
+    })
+    @interface RemoteBugreportNotificationType {}
+    private final DevicePolicyManagerService mService;
+    private final DevicePolicyManagerService.Injector mInjector;
+
+    private final AtomicBoolean mRemoteBugreportServiceIsActive = new AtomicBoolean();
+    private final AtomicBoolean mRemoteBugreportSharingAccepted = new AtomicBoolean();
+    private final Context mContext;
+
+    private final Handler mHandler;
+
+    private final Runnable mRemoteBugreportTimeoutRunnable = () -> {
+        if (mRemoteBugreportServiceIsActive.get()) {
+            onBugreportFailed();
+        }
+    };
+
+    private final BroadcastReceiver mRemoteBugreportFinishedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (ACTION_REMOTE_BUGREPORT_DISPATCH.equals(intent.getAction())
+                    && mRemoteBugreportServiceIsActive.get()) {
+                onBugreportFinished(intent);
+            }
+        }
+    };
+
+    private final BroadcastReceiver mRemoteBugreportConsentReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            mInjector.getNotificationManager().cancel(LOG_TAG, NOTIFICATION_ID);
+            if (ACTION_BUGREPORT_SHARING_ACCEPTED.equals(action)) {
+                onBugreportSharingAccepted();
+            } else if (ACTION_BUGREPORT_SHARING_DECLINED.equals(action)) {
+                onBugreportSharingDeclined();
+            }
+            mContext.unregisterReceiver(mRemoteBugreportConsentReceiver);
+        }
+    };
+
+    public RemoteBugreportManager(
+            DevicePolicyManagerService service, DevicePolicyManagerService.Injector injector) {
+        mService = service;
+        mInjector = injector;
+        mContext = service.mContext;
+        mHandler = service.mHandler;
+    }
+
+    private Notification buildNotification(@RemoteBugreportNotificationType int type) {
+        final Intent dialogIntent = new Intent(Settings.ACTION_SHOW_REMOTE_BUGREPORT_DIALOG);
+        dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        dialogIntent.putExtra(EXTRA_BUGREPORT_NOTIFICATION_TYPE, type);
+
+        // Fill the component explicitly to prevent the PendingIntent from being intercepted
+        // and fired with crafted target. b/155183624
+        final ActivityInfo targetInfo = dialogIntent.resolveActivityInfo(
+                mContext.getPackageManager(), PackageManager.MATCH_SYSTEM_ONLY);
+        if (targetInfo != null) {
+            dialogIntent.setComponent(targetInfo.getComponentName());
+        } else {
+            Slog.wtf(LOG_TAG, "Failed to resolve intent for remote bugreport dialog");
+        }
+
+        final PendingIntent pendingDialogIntent = PendingIntent.getActivityAsUser(mContext, type,
+                dialogIntent, 0, null, UserHandle.CURRENT);
+
+        final Notification.Builder builder =
+                new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN)
+                        .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+                        .setOngoing(true)
+                        .setLocalOnly(true)
+                        .setContentIntent(pendingDialogIntent)
+                        .setColor(mContext.getColor(
+                                com.android.internal.R.color.system_notification_accent_color));
+
+        if (type == NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED) {
+            builder.setContentTitle(mContext.getString(
+                        R.string.sharing_remote_bugreport_notification_title))
+                    .setProgress(0, 0, true);
+        } else if (type == NOTIFICATION_BUGREPORT_STARTED) {
+            builder.setContentTitle(mContext.getString(
+                        R.string.taking_remote_bugreport_notification_title))
+                    .setProgress(0, 0, true);
+        } else if (type == NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED) {
+            final PendingIntent pendingIntentAccept = PendingIntent.getBroadcast(mContext,
+                    NOTIFICATION_ID, new Intent(ACTION_BUGREPORT_SHARING_ACCEPTED),
+                    PendingIntent.FLAG_CANCEL_CURRENT);
+            final PendingIntent pendingIntentDecline = PendingIntent.getBroadcast(mContext,
+                    NOTIFICATION_ID, new Intent(ACTION_BUGREPORT_SHARING_DECLINED),
+                    PendingIntent.FLAG_CANCEL_CURRENT);
+            builder.addAction(new Notification.Action.Builder(null /* icon */, mContext.getString(
+                        R.string.decline_remote_bugreport_action), pendingIntentDecline).build())
+                    .addAction(new Notification.Action.Builder(null /* icon */, mContext.getString(
+                        R.string.share_remote_bugreport_action), pendingIntentAccept).build())
+                    .setContentTitle(mContext.getString(
+                        R.string.share_remote_bugreport_notification_title))
+                    .setContentText(mContext.getString(
+                        R.string.share_remote_bugreport_notification_message_finished))
+                    .setStyle(new Notification.BigTextStyle().bigText(mContext.getString(
+                        R.string.share_remote_bugreport_notification_message_finished)));
+        }
+
+        return builder.build();
+    }
+
+    /**
+     * Initiates bugreport collection.
+     * @return whether collection was initiated successfully.
+     */
+    public boolean requestBugreport() {
+        if (mRemoteBugreportServiceIsActive.get()
+                || (mService.getDeviceOwnerRemoteBugreportUriAndHash() != null)) {
+            Slog.d(LOG_TAG, "Remote bugreport wasn't started because there's already one running.");
+            return false;
+        }
+
+        final long callingIdentity = mInjector.binderClearCallingIdentity();
+        try {
+            mInjector.getIActivityManager().requestRemoteBugReport();
+
+            mRemoteBugreportServiceIsActive.set(true);
+            mRemoteBugreportSharingAccepted.set(false);
+            registerRemoteBugreportReceivers();
+            mInjector.getNotificationManager().notifyAsUser(LOG_TAG, NOTIFICATION_ID,
+                    buildNotification(NOTIFICATION_BUGREPORT_STARTED), UserHandle.ALL);
+            mHandler.postDelayed(mRemoteBugreportTimeoutRunnable, REMOTE_BUGREPORT_TIMEOUT_MILLIS);
+            return true;
+        } catch (RemoteException re) {
+            // should never happen
+            Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re);
+            return false;
+        } finally {
+            mInjector.binderRestoreCallingIdentity(callingIdentity);
+        }
+    }
+
+    private void registerRemoteBugreportReceivers() {
+        try {
+            final IntentFilter filterFinished =
+                    new IntentFilter(ACTION_REMOTE_BUGREPORT_DISPATCH, BUGREPORT_MIMETYPE);
+            mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished);
+        } catch (IntentFilter.MalformedMimeTypeException e) {
+            // should never happen, as setting a constant
+            Slog.w(LOG_TAG, "Failed to set type " + BUGREPORT_MIMETYPE, e);
+        }
+        final IntentFilter filterConsent = new IntentFilter();
+        filterConsent.addAction(ACTION_BUGREPORT_SHARING_DECLINED);
+        filterConsent.addAction(ACTION_BUGREPORT_SHARING_ACCEPTED);
+        mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
+    }
+
+    private void onBugreportFinished(Intent intent) {
+        mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable);
+        mRemoteBugreportServiceIsActive.set(false);
+        final Uri bugreportUri = intent.getData();
+        String bugreportUriString = null;
+        if (bugreportUri != null) {
+            bugreportUriString = bugreportUri.toString();
+        }
+        final String bugreportHash = intent.getStringExtra(EXTRA_REMOTE_BUGREPORT_HASH);
+        if (mRemoteBugreportSharingAccepted.get()) {
+            shareBugreportWithDeviceOwnerIfExists(bugreportUriString, bugreportHash);
+            mInjector.getNotificationManager().cancel(LOG_TAG,
+                    NOTIFICATION_ID);
+        } else {
+            mService.setDeviceOwnerRemoteBugreportUriAndHash(bugreportUriString, bugreportHash);
+            mInjector.getNotificationManager().notifyAsUser(LOG_TAG, NOTIFICATION_ID,
+                    buildNotification(NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED),
+                    UserHandle.ALL);
+        }
+        mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
+    }
+
+    private void onBugreportFailed() {
+        mRemoteBugreportServiceIsActive.set(false);
+        mInjector.systemPropertiesSet(CTL_STOP, REMOTE_BUGREPORT_SERVICE);
+        mRemoteBugreportSharingAccepted.set(false);
+        mService.setDeviceOwnerRemoteBugreportUriAndHash(null, null);
+        mInjector.getNotificationManager().cancel(LOG_TAG, NOTIFICATION_ID);
+        final Bundle extras = new Bundle();
+        extras.putInt(DeviceAdminReceiver.EXTRA_BUGREPORT_FAILURE_REASON,
+                DeviceAdminReceiver.BUGREPORT_FAILURE_FAILED_COMPLETING);
+        mService.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_FAILED, extras);
+        mContext.unregisterReceiver(mRemoteBugreportConsentReceiver);
+        mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
+    }
+
+    private void onBugreportSharingAccepted() {
+        mRemoteBugreportSharingAccepted.set(true);
+        final Pair<String, String> uriAndHash = mService.getDeviceOwnerRemoteBugreportUriAndHash();
+        if (uriAndHash != null) {
+            shareBugreportWithDeviceOwnerIfExists(uriAndHash.first, uriAndHash.second);
+        } else if (mRemoteBugreportServiceIsActive.get()) {
+            mInjector.getNotificationManager().notifyAsUser(LOG_TAG, NOTIFICATION_ID,
+                    buildNotification(NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED),
+                    UserHandle.ALL);
+        }
+    }
+
+    private void onBugreportSharingDeclined() {
+        if (mRemoteBugreportServiceIsActive.get()) {
+            mInjector.systemPropertiesSet(CTL_STOP,
+                    REMOTE_BUGREPORT_SERVICE);
+            mRemoteBugreportServiceIsActive.set(false);
+            mHandler.removeCallbacks(mRemoteBugreportTimeoutRunnable);
+            mContext.unregisterReceiver(mRemoteBugreportFinishedReceiver);
+        }
+        mRemoteBugreportSharingAccepted.set(false);
+        mService.setDeviceOwnerRemoteBugreportUriAndHash(null, null);
+        mService.sendDeviceOwnerCommand(
+                DeviceAdminReceiver.ACTION_BUGREPORT_SHARING_DECLINED, null);
+    }
+
+    private void shareBugreportWithDeviceOwnerIfExists(
+            String bugreportUriString, String bugreportHash) {
+        try {
+            if (bugreportUriString == null) {
+                throw new FileNotFoundException();
+            }
+            final Uri bugreportUri = Uri.parse(bugreportUriString);
+            mService.sendBugreportToDeviceOwner(bugreportUri, bugreportHash);
+        } catch (FileNotFoundException e) {
+            final Bundle extras = new Bundle();
+            extras.putInt(DeviceAdminReceiver.EXTRA_BUGREPORT_FAILURE_REASON,
+                    DeviceAdminReceiver.BUGREPORT_FAILURE_FILE_NO_LONGER_AVAILABLE);
+            mService.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_BUGREPORT_FAILED, extras);
+        } finally {
+            mRemoteBugreportSharingAccepted.set(false);
+            mService.setDeviceOwnerRemoteBugreportUriAndHash(null, null);
+        }
+    }
+
+    /**
+     * Check if a bugreport was collected but not shared before reboot because the user didn't act
+     * upon sharing notification.
+     */
+    public void checkForPendingBugreportAfterBoot() {
+        if (mService.getDeviceOwnerRemoteBugreportUriAndHash() == null) {
+            return;
+        }
+        final IntentFilter filterConsent = new IntentFilter();
+        filterConsent.addAction(ACTION_BUGREPORT_SHARING_DECLINED);
+        filterConsent.addAction(ACTION_BUGREPORT_SHARING_ACCEPTED);
+        mContext.registerReceiver(mRemoteBugreportConsentReceiver, filterConsent);
+        mInjector.getNotificationManager().notifyAsUser(LOG_TAG, NOTIFICATION_ID,
+                buildNotification(NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED), UserHandle.ALL);
+    }
+}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
deleted file mode 100644
index 1630f27..0000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2016 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.devicepolicy;
-
-import android.annotation.IntDef;
-import android.app.Notification;
-import android.app.PendingIntent;
-import android.app.admin.DevicePolicyManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.text.format.DateUtils;
-import android.util.Slog;
-
-import com.android.internal.R;
-import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.notification.SystemNotificationChannels;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Utilities class for the remote bugreport operation.
- */
-class RemoteBugreportUtils {
-
-    private static final String TAG = "RemoteBugreportUtils";
-    static final int NOTIFICATION_ID = SystemMessage.NOTE_REMOTE_BUGREPORT;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-        DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED,
-        DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED,
-        DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED
-    })
-    @interface RemoteBugreportNotificationType {}
-
-    static final long REMOTE_BUGREPORT_TIMEOUT_MILLIS = 10 * DateUtils.MINUTE_IN_MILLIS;
-
-    static final String CTL_STOP = "ctl.stop";
-    static final String REMOTE_BUGREPORT_SERVICE = "bugreportd";
-
-    static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
-
-    static Notification buildNotification(Context context,
-            @RemoteBugreportNotificationType int type) {
-        Intent dialogIntent = new Intent(Settings.ACTION_SHOW_REMOTE_BUGREPORT_DIALOG);
-        dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        dialogIntent.putExtra(DevicePolicyManager.EXTRA_BUGREPORT_NOTIFICATION_TYPE, type);
-
-        // Fill the component explicitly to prevent the PendingIntent from being intercepted
-        // and fired with crafted target. b/155183624
-        ActivityInfo targetInfo = dialogIntent.resolveActivityInfo(
-                context.getPackageManager(), PackageManager.MATCH_SYSTEM_ONLY);
-        if (targetInfo != null) {
-            dialogIntent.setComponent(targetInfo.getComponentName());
-        } else {
-            Slog.wtf(TAG, "Failed to resolve intent for remote bugreport dialog");
-        }
-
-        PendingIntent pendingDialogIntent = PendingIntent.getActivityAsUser(context, type,
-                dialogIntent, 0, null, UserHandle.CURRENT);
-
-        Notification.Builder builder =
-                new Notification.Builder(context, SystemNotificationChannels.DEVICE_ADMIN)
-                        .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
-                        .setOngoing(true)
-                        .setLocalOnly(true)
-                        .setContentIntent(pendingDialogIntent)
-                        .setColor(context.getColor(
-                                com.android.internal.R.color.system_notification_accent_color));
-
-        if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEPTED_NOT_FINISHED) {
-            builder.setContentTitle(context.getString(
-                        R.string.sharing_remote_bugreport_notification_title))
-                    .setProgress(0, 0, true);
-        } else if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED) {
-            builder.setContentTitle(context.getString(
-                        R.string.taking_remote_bugreport_notification_title))
-                    .setProgress(0, 0, true);
-        } else if (type == DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED) {
-            PendingIntent pendingIntentAccept = PendingIntent.getBroadcast(context, NOTIFICATION_ID,
-                    new Intent(DevicePolicyManager.ACTION_BUGREPORT_SHARING_ACCEPTED),
-                    PendingIntent.FLAG_CANCEL_CURRENT);
-            PendingIntent pendingIntentDecline = PendingIntent.getBroadcast(context,
-                    NOTIFICATION_ID, new Intent(
-                            DevicePolicyManager.ACTION_BUGREPORT_SHARING_DECLINED),
-                    PendingIntent.FLAG_CANCEL_CURRENT);
-            builder.addAction(new Notification.Action.Builder(null /* icon */, context.getString(
-                        R.string.decline_remote_bugreport_action), pendingIntentDecline).build())
-                    .addAction(new Notification.Action.Builder(null /* icon */, context.getString(
-                        R.string.share_remote_bugreport_action), pendingIntentAccept).build())
-                    .setContentTitle(context.getString(
-                        R.string.share_remote_bugreport_notification_title))
-                    .setContentText(context.getString(
-                        R.string.share_remote_bugreport_notification_message_finished))
-                    .setStyle(new Notification.BigTextStyle().bigText(context.getString(
-                        R.string.share_remote_bugreport_notification_message_finished)));
-        }
-
-        return builder.build();
-    }
-}
-
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 0ae10b6..41945a2 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -237,11 +237,9 @@
     return ok();
 }
 
-binder::Status BinderIncrementalService::isFileRangeLoaded(int32_t storageId,
-                                                           const std::string& path, int64_t start,
-                                                           int64_t end, bool* _aidl_return) {
-    // TODO: implement
-    *_aidl_return = false;
+binder::Status BinderIncrementalService::getLoadingProgress(int32_t storageId,
+                                                            float* _aidl_return) {
+    *_aidl_return = mImpl.getLoadingProgress(storageId);
     return ok();
 }
 
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 1015494..8b40350 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -66,8 +66,7 @@
                             int32_t destStorageId, const std::string& destPath,
                             int32_t* _aidl_return) final;
     binder::Status unlink(int32_t storageId, const std::string& path, int32_t* _aidl_return) final;
-    binder::Status isFileRangeLoaded(int32_t storageId, const std::string& path, int64_t start,
-                                     int64_t end, bool* _aidl_return) final;
+    binder::Status getLoadingProgress(int32_t storageId, float* _aidl_return) final;
     binder::Status getMetadataByPath(int32_t storageId, const std::string& path,
                                      std::vector<uint8_t>* _aidl_return) final;
     binder::Status getMetadataById(int32_t storageId, const std::vector<uint8_t>& id,
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index f7082a9..9836262e 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -37,7 +37,6 @@
 #include "Metadata.pb.h"
 
 using namespace std::literals;
-namespace fs = std::filesystem;
 
 constexpr const char* kDataUsageStats = "android.permission.LOADER_USAGE_STATS";
 constexpr const char* kOpUsage = "android:loader_usage_stats";
@@ -276,6 +275,7 @@
         mJni(sm.getJni()),
         mLooper(sm.getLooper()),
         mTimedQueue(sm.getTimedQueue()),
+        mFs(sm.getFs()),
         mIncrementalDir(rootDir) {
     CHECK(mVold) << "Vold service is unavailable";
     CHECK(mDataLoaderManager) << "DataLoaderManagerService is unavailable";
@@ -283,6 +283,7 @@
     CHECK(mJni) << "JNI is unavailable";
     CHECK(mLooper) << "Looper is unavailable";
     CHECK(mTimedQueue) << "TimedQueue is unavailable";
+    CHECK(mFs) << "Fs is unavailable";
 
     mJobQueue.reserve(16);
     mJobProcessor = std::thread([this]() {
@@ -344,7 +345,8 @@
             }
             dprintf(fd, "    storages (%d): {\n", int(mnt.storages.size()));
             for (auto&& [storageId, storage] : mnt.storages) {
-                dprintf(fd, "      [%d] -> [%s]\n", storageId, storage.name.c_str());
+                dprintf(fd, "      [%d] -> [%s] (%d %% loaded) \n", storageId, storage.name.c_str(),
+                        (int)(getLoadingProgressFromPath(mnt, storage.name.c_str()) * 100));
             }
             dprintf(fd, "    }\n");
 
@@ -1671,6 +1673,45 @@
     return mRunning;
 }
 
+float IncrementalService::getLoadingProgress(StorageId storage) const {
+    std::unique_lock l(mLock);
+    const auto ifs = getIfsLocked(storage);
+    if (!ifs) {
+        LOG(ERROR) << "getLoadingProgress failed, invalid storageId: " << storage;
+        return -EINVAL;
+    }
+    const auto storageInfo = ifs->storages.find(storage);
+    if (storageInfo == ifs->storages.end()) {
+        LOG(ERROR) << "getLoadingProgress failed, no storage: " << storage;
+        return -EINVAL;
+    }
+    l.unlock();
+    return getLoadingProgressFromPath(*ifs, storageInfo->second.name);
+}
+
+float IncrementalService::getLoadingProgressFromPath(const IncFsMount& ifs,
+                                                     std::string_view storagePath) const {
+    size_t totalBlocks = 0, filledBlocks = 0;
+    const auto filePaths = mFs->listFilesRecursive(storagePath);
+    for (const auto& filePath : filePaths) {
+        const auto [filledBlocksCount, totalBlocksCount] =
+                mIncFs->countFilledBlocks(ifs.control, filePath);
+        if (filledBlocksCount < 0) {
+            LOG(ERROR) << "getLoadingProgress failed to get filled blocks count for: " << filePath
+                       << " errno: " << filledBlocksCount;
+            return filledBlocksCount;
+        }
+        totalBlocks += totalBlocksCount;
+        filledBlocks += filledBlocksCount;
+    }
+
+    if (totalBlocks == 0) {
+        // No file in the storage or files are empty; regarded as fully loaded
+        return 1;
+    }
+    return (float)filledBlocks / (float)totalBlocks;
+}
+
 bool IncrementalService::perfLoggingEnabled() {
     static const bool enabled = base::GetBoolProperty("incremental.perflogging", false);
     return enabled;
@@ -2029,11 +2070,13 @@
 
         // Healthcheck depends on timestamp of the oldest pending read.
         // To get it, we need to re-open a pendingReads FD to get a full list of reads.
-        // Additionally we need to re-register for epoll with fresh FDs in case there are no reads.
+        // Additionally we need to re-register for epoll with fresh FDs in case there are no
+        // reads.
         const auto now = Clock::now();
         const auto kernelTsUs = getOldestPendingReadTs();
         if (baseline) {
-            // Updating baseline only on looper/epoll callback, i.e. on new set of pending reads.
+            // Updating baseline only on looper/epoll callback, i.e. on new set of pending
+            // reads.
             mHealthBase = {now, kernelTsUs};
         }
 
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index a6cc946..cd6bfed 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -132,9 +132,7 @@
              std::string_view newPath);
     int unlink(StorageId storage, std::string_view path);
 
-    bool isRangeLoaded(StorageId storage, FileId file, std::pair<BlockIndex, BlockIndex> range) {
-        return false;
-    }
+    float getLoadingProgress(StorageId storage) const;
 
     RawMetadata getMetadata(StorageId storage, std::string_view path) const;
     RawMetadata getMetadata(StorageId storage, FileId node) const;
@@ -341,6 +339,8 @@
     int makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path, int mode);
     binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);
 
+    float getLoadingProgressFromPath(const IncFsMount& ifs, std::string_view path) const;
+
     void registerAppOpsCallback(const std::string& packageName);
     bool unregisterAppOpsCallback(const std::string& packageName);
     void onAppOpChanged(const std::string& packageName);
@@ -363,6 +363,7 @@
     const std::unique_ptr<JniWrapper> mJni;
     const std::unique_ptr<LooperWrapper> mLooper;
     const std::unique_ptr<TimedQueueWrapper> mTimedQueue;
+    const std::unique_ptr<FsWrapper> mFs;
     const std::string mIncrementalDir;
 
     mutable std::mutex mLock;
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 99a35ad..1ed46c4 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -25,6 +25,7 @@
 #include <binder/AppOpsManager.h>
 #include <utils/String16.h>
 
+#include <filesystem>
 #include <thread>
 
 #include "IncrementalServiceValidation.h"
@@ -165,6 +166,29 @@
     FileId getFileId(const Control& control, std::string_view path) const final {
         return incfs::getFileId(control, path);
     }
+    std::pair<IncFsBlockIndex, IncFsBlockIndex> countFilledBlocks(
+            const Control& control, std::string_view path) const final {
+        const auto fileId = incfs::getFileId(control, path);
+        const auto fd = incfs::openForSpecialOps(control, fileId);
+        int res = fd.get();
+        if (!fd.ok()) {
+            return {res, res};
+        }
+        const auto ranges = incfs::getFilledRanges(res);
+        res = ranges.first;
+        if (res) {
+            return {res, res};
+        }
+        const auto totalBlocksCount = ranges.second.internalRawRanges().endIndex;
+        int filledBlockCount = 0;
+        for (const auto& dataRange : ranges.second.dataRanges()) {
+            filledBlockCount += dataRange.size();
+        }
+        for (const auto& hashRange : ranges.second.hashRanges()) {
+            filledBlockCount += hashRange.size();
+        }
+        return {filledBlockCount, totalBlocksCount};
+    }
     ErrorCode link(const Control& control, std::string_view from, std::string_view to) const final {
         return incfs::link(control, from, to);
     }
@@ -265,6 +289,23 @@
     std::thread mThread;
 };
 
+class RealFsWrapper : public FsWrapper {
+public:
+    RealFsWrapper() = default;
+    ~RealFsWrapper() = default;
+
+    std::vector<std::string> listFilesRecursive(std::string_view directoryPath) const final {
+        std::vector<std::string> files;
+        for (const auto& entry : std::filesystem::recursive_directory_iterator(directoryPath)) {
+            if (!entry.is_regular_file()) {
+                continue;
+            }
+            files.push_back(entry.path().c_str());
+        }
+        return files;
+    }
+};
+
 RealServiceManager::RealServiceManager(sp<IServiceManager> serviceManager, JNIEnv* env)
       : mServiceManager(std::move(serviceManager)), mJvm(RealJniWrapper::getJvm(env)) {}
 
@@ -316,6 +357,10 @@
     return std::make_unique<RealTimedQueueWrapper>(mJvm);
 }
 
+std::unique_ptr<FsWrapper> RealServiceManager::getFs() {
+    return std::make_unique<RealFsWrapper>();
+}
+
 static JavaVM* getJavaVm(JNIEnv* env) {
     CHECK(env);
     JavaVM* jvm = nullptr;
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 8cd726fd..82a1704 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -91,6 +91,8 @@
     virtual incfs::RawMetadata getMetadata(const Control& control, FileId fileid) const = 0;
     virtual incfs::RawMetadata getMetadata(const Control& control, std::string_view path) const = 0;
     virtual FileId getFileId(const Control& control, std::string_view path) const = 0;
+    virtual std::pair<IncFsBlockIndex, IncFsBlockIndex> countFilledBlocks(
+            const Control& control, std::string_view path) const = 0;
     virtual ErrorCode link(const Control& control, std::string_view from,
                            std::string_view to) const = 0;
     virtual ErrorCode unlink(const Control& control, std::string_view path) const = 0;
@@ -106,7 +108,8 @@
     virtual ~AppOpsManagerWrapper() = default;
     virtual binder::Status checkPermission(const char* permission, const char* operation,
                                            const char* package) const = 0;
-    virtual void startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback) = 0;
+    virtual void startWatchingMode(int32_t op, const String16& packageName,
+                                   const sp<IAppOpsCallback>& callback) = 0;
     virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
 };
 
@@ -134,6 +137,12 @@
     virtual void stop() = 0;
 };
 
+class FsWrapper {
+public:
+    virtual ~FsWrapper() = default;
+    virtual std::vector<std::string> listFilesRecursive(std::string_view directoryPath) const = 0;
+};
+
 class ServiceManagerWrapper {
 public:
     virtual ~ServiceManagerWrapper() = default;
@@ -144,6 +153,7 @@
     virtual std::unique_ptr<JniWrapper> getJni() = 0;
     virtual std::unique_ptr<LooperWrapper> getLooper() = 0;
     virtual std::unique_ptr<TimedQueueWrapper> getTimedQueue() = 0;
+    virtual std::unique_ptr<FsWrapper> getFs() = 0;
 };
 
 // --- Real stuff ---
@@ -159,6 +169,7 @@
     std::unique_ptr<JniWrapper> getJni() final;
     std::unique_ptr<LooperWrapper> getLooper() final;
     std::unique_ptr<TimedQueueWrapper> getTimedQueue() final;
+    std::unique_ptr<FsWrapper> getFs() final;
 
 private:
     template <class INTERFACE>
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 1ae9e25..d1000e56 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -54,8 +54,10 @@
     MOCK_CONST_METHOD1(unmountIncFs, binder::Status(const std::string& dir));
     MOCK_CONST_METHOD2(bindMount,
                        binder::Status(const std::string& sourceDir, const std::string& argetDir));
-    MOCK_CONST_METHOD2(setIncFsMountOptions,
-                       binder::Status(const ::android::os::incremental::IncrementalFileSystemControlParcel&, bool));
+    MOCK_CONST_METHOD2(
+            setIncFsMountOptions,
+            binder::Status(const ::android::os::incremental::IncrementalFileSystemControlParcel&,
+                           bool));
 
     void mountIncFsFails() {
         ON_CALL(*this, mountIncFs(_, _, _, _))
@@ -80,8 +82,8 @@
     }
     void setIncFsMountOptionsFails() const {
         ON_CALL(*this, setIncFsMountOptions(_, _))
-                .WillByDefault(
-                        Return(binder::Status::fromExceptionCode(1, String8("failed to set options"))));
+                .WillByDefault(Return(
+                        binder::Status::fromExceptionCode(1, String8("failed to set options"))));
     }
     void setIncFsMountOptionsSuccess() {
         ON_CALL(*this, setIncFsMountOptions(_, _)).WillByDefault(Return(binder::Status::ok()));
@@ -280,8 +282,12 @@
     MOCK_CONST_METHOD2(getMetadata, RawMetadata(const Control& control, FileId fileid));
     MOCK_CONST_METHOD2(getMetadata, RawMetadata(const Control& control, std::string_view path));
     MOCK_CONST_METHOD2(getFileId, FileId(const Control& control, std::string_view path));
+    MOCK_CONST_METHOD2(countFilledBlocks,
+                       std::pair<IncFsBlockIndex, IncFsBlockIndex>(const Control& control,
+                                                                   std::string_view path));
     MOCK_CONST_METHOD3(link,
-                       ErrorCode(const Control& control, std::string_view from, std::string_view to));
+                       ErrorCode(const Control& control, std::string_view from,
+                                 std::string_view to));
     MOCK_CONST_METHOD2(unlink, ErrorCode(const Control& control, std::string_view path));
     MOCK_CONST_METHOD2(openForSpecialOps, base::unique_fd(const Control& control, FileId id));
     MOCK_CONST_METHOD1(writeBlocks, ErrorCode(std::span<const DataBlock> blocks));
@@ -293,6 +299,19 @@
 
     void makeFileFails() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(-1)); }
     void makeFileSuccess() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(0)); }
+
+    void countFilledBlocksSuccess() {
+        ON_CALL(*this, countFilledBlocks(_, _)).WillByDefault(Return(std::make_pair(1, 2)));
+    }
+
+    void countFilledBlocksFails() {
+        ON_CALL(*this, countFilledBlocks(_, _)).WillByDefault(Return(std::make_pair(-1, -1)));
+    }
+
+    void countFilledBlocksEmpty() {
+        ON_CALL(*this, countFilledBlocks(_, _)).WillByDefault(Return(std::make_pair(0, 0)));
+    }
+
     void openMountSuccess() {
         ON_CALL(*this, openMount(_)).WillByDefault(Invoke(this, &MockIncFs::openMountForHealth));
     }
@@ -447,6 +466,21 @@
     Job mWhat;
 };
 
+class MockFsWrapper : public FsWrapper {
+public:
+    MOCK_CONST_METHOD1(listFilesRecursive, std::vector<std::string>(std::string_view));
+    void hasNoFile() {
+        ON_CALL(*this, listFilesRecursive(_)).WillByDefault(Return(std::vector<std::string>()));
+    }
+    void hasFiles() {
+        ON_CALL(*this, listFilesRecursive(_))
+                .WillByDefault(Invoke(this, &MockFsWrapper::fakeFiles));
+    }
+    std::vector<std::string> fakeFiles(std::string_view directoryPath) {
+        return {"base.apk", "split.apk", "lib/a.so"};
+    }
+};
+
 class MockStorageHealthListener : public os::incremental::BnStorageHealthListener {
 public:
     MOCK_METHOD2(onHealthStatus, binder::Status(int32_t storageId, int32_t status));
@@ -474,23 +508,28 @@
                        std::unique_ptr<MockAppOpsManager> appOpsManager,
                        std::unique_ptr<MockJniWrapper> jni,
                        std::unique_ptr<MockLooperWrapper> looper,
-                       std::unique_ptr<MockTimedQueueWrapper> timedQueue)
+                       std::unique_ptr<MockTimedQueueWrapper> timedQueue,
+                       std::unique_ptr<MockFsWrapper> fs)
           : mVold(std::move(vold)),
             mDataLoaderManager(std::move(dataLoaderManager)),
             mIncFs(std::move(incfs)),
             mAppOpsManager(std::move(appOpsManager)),
             mJni(std::move(jni)),
             mLooper(std::move(looper)),
-            mTimedQueue(std::move(timedQueue)) {}
+            mTimedQueue(std::move(timedQueue)),
+            mFs(std::move(fs)) {}
     std::unique_ptr<VoldServiceWrapper> getVoldService() final { return std::move(mVold); }
     std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() final {
         return std::move(mDataLoaderManager);
     }
     std::unique_ptr<IncFsWrapper> getIncFs() final { return std::move(mIncFs); }
-    std::unique_ptr<AppOpsManagerWrapper> getAppOpsManager() final { return std::move(mAppOpsManager); }
+    std::unique_ptr<AppOpsManagerWrapper> getAppOpsManager() final {
+        return std::move(mAppOpsManager);
+    }
     std::unique_ptr<JniWrapper> getJni() final { return std::move(mJni); }
     std::unique_ptr<LooperWrapper> getLooper() final { return std::move(mLooper); }
     std::unique_ptr<TimedQueueWrapper> getTimedQueue() final { return std::move(mTimedQueue); }
+    std::unique_ptr<FsWrapper> getFs() final { return std::move(mFs); }
 
 private:
     std::unique_ptr<MockVoldService> mVold;
@@ -500,6 +539,7 @@
     std::unique_ptr<MockJniWrapper> mJni;
     std::unique_ptr<MockLooperWrapper> mLooper;
     std::unique_ptr<MockTimedQueueWrapper> mTimedQueue;
+    std::unique_ptr<MockFsWrapper> mFs;
 };
 
 // --- IncrementalServiceTest ---
@@ -523,6 +563,8 @@
         mLooper = looper.get();
         auto timedQueue = std::make_unique<NiceMock<MockTimedQueueWrapper>>();
         mTimedQueue = timedQueue.get();
+        auto fs = std::make_unique<NiceMock<MockFsWrapper>>();
+        mFs = fs.get();
         mIncrementalService =
                 std::make_unique<IncrementalService>(MockServiceManager(std::move(vold),
                                                                         std::move(
@@ -531,12 +573,14 @@
                                                                         std::move(appOps),
                                                                         std::move(jni),
                                                                         std::move(looper),
-                                                                        std::move(timedQueue)),
+                                                                        std::move(timedQueue),
+                                                                        std::move(fs)),
                                                      mRootDir.path);
         mDataLoaderParcel.packageName = "com.test";
         mDataLoaderParcel.arguments = "uri";
         mDataLoaderManager->unbindFromDataLoaderSuccess();
         mIncrementalService->onSystemReady();
+        setupSuccess();
     }
 
     void setUpExistingMountDir(const std::string& rootDir) {
@@ -560,6 +604,14 @@
                 .WillByDefault(Invoke(mIncFs, &MockIncFs::getStorageMetadata));
     }
 
+    void setupSuccess() {
+        mVold->mountIncFsSuccess();
+        mIncFs->makeFileSuccess();
+        mVold->bindMountSuccess();
+        mDataLoaderManager->bindToDataLoaderSuccess();
+        mDataLoaderManager->getDataLoaderSuccess();
+    }
+
 protected:
     NiceMock<MockVoldService>* mVold = nullptr;
     NiceMock<MockIncFs>* mIncFs = nullptr;
@@ -568,6 +620,7 @@
     NiceMock<MockJniWrapper>* mJni = nullptr;
     NiceMock<MockLooperWrapper>* mLooper = nullptr;
     NiceMock<MockTimedQueueWrapper>* mTimedQueue = nullptr;
+    NiceMock<MockFsWrapper>* mFs = nullptr;
     NiceMock<MockDataLoader>* mDataLoader = nullptr;
     std::unique_ptr<IncrementalService> mIncrementalService;
     TemporaryDir mRootDir;
@@ -641,11 +694,6 @@
 }
 
 TEST_F(IncrementalServiceTest, testDeleteStorageSuccess) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
-    mVold->bindMountSuccess();
-    mDataLoaderManager->bindToDataLoaderSuccess();
-    mDataLoaderManager->getDataLoaderSuccess();
     EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
     EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
@@ -661,11 +709,6 @@
 }
 
 TEST_F(IncrementalServiceTest, testDataLoaderDestroyed) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
-    mVold->bindMountSuccess();
-    mDataLoaderManager->bindToDataLoaderSuccess();
-    mDataLoaderManager->getDataLoaderSuccess();
     EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(2);
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
     EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2);
@@ -682,12 +725,7 @@
 }
 
 TEST_F(IncrementalServiceTest, testStartDataLoaderCreate) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
-    mVold->bindMountSuccess();
     mDataLoader->initializeCreateOkNoStatus();
-    mDataLoaderManager->bindToDataLoaderSuccess();
-    mDataLoaderManager->getDataLoaderSuccess();
     EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
     EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
@@ -705,12 +743,7 @@
 }
 
 TEST_F(IncrementalServiceTest, testStartDataLoaderPendingStart) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
-    mVold->bindMountSuccess();
     mDataLoader->initializeCreateOkNoStatus();
-    mDataLoaderManager->bindToDataLoaderSuccess();
-    mDataLoaderManager->getDataLoaderSuccess();
     EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
     EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2);
@@ -727,12 +760,7 @@
 }
 
 TEST_F(IncrementalServiceTest, testStartDataLoaderCreateUnavailable) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
-    mVold->bindMountSuccess();
     mDataLoader->initializeCreateOkNoStatus();
-    mDataLoaderManager->bindToDataLoaderSuccess();
-    mDataLoaderManager->getDataLoaderSuccess();
     EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
     EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
@@ -748,14 +776,10 @@
 }
 
 TEST_F(IncrementalServiceTest, testStartDataLoaderRecreateOnPendingReads) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
-    mIncFs->openMountSuccess();
     mIncFs->waitForPendingReadsSuccess();
-    mVold->bindMountSuccess();
+    mIncFs->openMountSuccess();
     mDataLoader->initializeCreateOkNoStatus();
-    mDataLoaderManager->bindToDataLoaderSuccess();
-    mDataLoaderManager->getDataLoaderSuccess();
+
     EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(2);
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(2);
     EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2);
@@ -776,12 +800,8 @@
 }
 
 TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
     mIncFs->openMountSuccess();
-    mVold->bindMountSuccess();
-    mDataLoaderManager->bindToDataLoaderSuccess();
-    mDataLoaderManager->getDataLoaderSuccess();
+
     EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1);
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1);
     EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1);
@@ -906,13 +926,9 @@
 }
 
 TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
-    mVold->bindMountSuccess();
     mVold->setIncFsMountOptionsSuccess();
-    mDataLoaderManager->bindToDataLoaderSuccess();
-    mDataLoaderManager->getDataLoaderSuccess();
     mAppOpsManager->checkPermissionSuccess();
+
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // We are calling setIncFsMountOptions(true).
@@ -930,13 +946,9 @@
 }
 
 TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndDisabled) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
-    mVold->bindMountSuccess();
     mVold->setIncFsMountOptionsSuccess();
-    mDataLoaderManager->bindToDataLoaderSuccess();
-    mDataLoaderManager->getDataLoaderSuccess();
     mAppOpsManager->checkPermissionSuccess();
+
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // Enabling and then disabling readlogs.
@@ -958,14 +970,10 @@
 }
 
 TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccessAndPermissionChanged) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
-    mVold->bindMountSuccess();
     mVold->setIncFsMountOptionsSuccess();
-    mDataLoaderManager->bindToDataLoaderSuccess();
-    mDataLoaderManager->getDataLoaderSuccess();
     mAppOpsManager->checkPermissionSuccess();
     mAppOpsManager->initializeStartWatchingMode();
+
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // We are calling setIncFsMountOptions(true).
@@ -987,12 +995,8 @@
 }
 
 TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsCheckPermissionFails) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
-    mVold->bindMountSuccess();
-    mDataLoaderManager->bindToDataLoaderSuccess();
-    mDataLoaderManager->getDataLoaderSuccess();
     mAppOpsManager->checkPermissionFails();
+
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // checkPermission fails, no calls to set opitions,  start or stop WatchingMode.
@@ -1008,13 +1012,9 @@
 }
 
 TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsFails) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
-    mVold->bindMountSuccess();
     mVold->setIncFsMountOptionsFails();
-    mDataLoaderManager->bindToDataLoaderSuccess();
-    mDataLoaderManager->getDataLoaderSuccess();
     mAppOpsManager->checkPermissionSuccess();
+
     EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_));
     EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
     // We are calling setIncFsMountOptions.
@@ -1031,11 +1031,6 @@
 }
 
 TEST_F(IncrementalServiceTest, testMakeDirectory) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
-    mVold->bindMountSuccess();
-    mDataLoaderManager->bindToDataLoaderSuccess();
-    mDataLoaderManager->getDataLoaderSuccess();
     TemporaryDir tempDir;
     int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
                                                        IncrementalService::CreateOptions::CreateNew,
@@ -1055,11 +1050,6 @@
 }
 
 TEST_F(IncrementalServiceTest, testMakeDirectories) {
-    mVold->mountIncFsSuccess();
-    mIncFs->makeFileSuccess();
-    mVold->bindMountSuccess();
-    mDataLoaderManager->bindToDataLoaderSuccess();
-    mDataLoaderManager->getDataLoaderSuccess();
     TemporaryDir tempDir;
     int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
                                                        IncrementalService::CreateOptions::CreateNew,
@@ -1078,4 +1068,51 @@
     auto res = mIncrementalService->makeDirs(storageId, dir_path, 0555);
     ASSERT_EQ(res, 0);
 }
+
+TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithNoFile) {
+    mIncFs->countFilledBlocksSuccess();
+    mFs->hasNoFile();
+
+    TemporaryDir tempDir;
+    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+                                                       IncrementalService::CreateOptions::CreateNew,
+                                                       {}, {}, {});
+    ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId));
+}
+
+TEST_F(IncrementalServiceTest, testGetLoadingProgressFailsWithFailedRanges) {
+    mIncFs->countFilledBlocksFails();
+    mFs->hasFiles();
+
+    TemporaryDir tempDir;
+    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+                                                       IncrementalService::CreateOptions::CreateNew,
+                                                       {}, {}, {});
+    EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(1);
+    ASSERT_EQ(-1, mIncrementalService->getLoadingProgress(storageId));
+}
+
+TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccessWithEmptyRanges) {
+    mIncFs->countFilledBlocksEmpty();
+    mFs->hasFiles();
+
+    TemporaryDir tempDir;
+    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+                                                       IncrementalService::CreateOptions::CreateNew,
+                                                       {}, {}, {});
+    EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3);
+    ASSERT_EQ(1, mIncrementalService->getLoadingProgress(storageId));
+}
+
+TEST_F(IncrementalServiceTest, testGetLoadingProgressSuccess) {
+    mIncFs->countFilledBlocksSuccess();
+    mFs->hasFiles();
+
+    TemporaryDir tempDir;
+    int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+                                                       IncrementalService::CreateOptions::CreateNew,
+                                                       {}, {}, {});
+    EXPECT_CALL(*mIncFs, countFilledBlocks(_, _)).Times(3);
+    ASSERT_EQ(0.5, mIncrementalService->getLoadingProgress(storageId));
+}
 } // namespace android::os::incremental
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index 4b25890..5fa809f 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -204,10 +204,10 @@
                 CROSS_PROFILE_APP_PACKAGE_NAME, PERSONAL_PROFILE_UID, PERSONAL_PROFILE_USER_ID);
         ShadowApplicationPackageManager.setPackageUidAsUser(
                 CROSS_PROFILE_APP_PACKAGE_NAME, WORK_PROFILE_UID, WORK_PROFILE_USER_ID);
-        when(mPackageManagerInternal.getPackageUidInternal(
+        when(mPackageManagerInternal.getPackageUid(
                 CROSS_PROFILE_APP_PACKAGE_NAME, /* flags= */ 0, PERSONAL_PROFILE_USER_ID))
                 .thenReturn(PERSONAL_PROFILE_UID);
-        when(mPackageManagerInternal.getPackageUidInternal(
+        when(mPackageManagerInternal.getPackageUid(
                 CROSS_PROFILE_APP_PACKAGE_NAME, /* flags= */ 0, WORK_PROFILE_USER_ID))
                 .thenReturn(WORK_PROFILE_UID);
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
index fdcadf3..d6894cf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java
@@ -33,6 +33,7 @@
 import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
 import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
 import static com.android.server.location.LocationUtils.createLocation;
+import static com.android.server.location.listeners.RemoteListenerRegistration.IN_PROCESS_EXECUTOR;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -85,7 +86,6 @@
 import com.android.internal.location.ProviderRequest;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
-import com.android.server.location.listeners.ListenerRegistration;
 import com.android.server.location.util.FakeUserInfoHelper;
 import com.android.server.location.util.TestInjector;
 
@@ -484,7 +484,7 @@
                 PERMISSION_FINE, listener);
 
         CountDownLatch blocker = new CountDownLatch(1);
-        ListenerRegistration.IN_PROCESS_EXECUTOR.execute(() -> {
+        IN_PROCESS_EXECUTOR.execute(() -> {
             try {
                 blocker.await();
             } catch (InterruptedException e) {
@@ -622,7 +622,7 @@
                 PERMISSION_FINE, listener);
 
         CountDownLatch blocker = new CountDownLatch(1);
-        ListenerRegistration.IN_PROCESS_EXECUTOR.execute(() -> {
+        IN_PROCESS_EXECUTOR.execute(() -> {
             try {
                 blocker.await();
             } catch (InterruptedException e) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
index 1ef1255..69a9f44 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.location.listeners;
 
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -27,8 +29,6 @@
 import static org.mockito.Mockito.verify;
 import static org.testng.Assert.assertThrows;
 
-import android.location.util.identity.CallerIdentity;
-import android.os.Process;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
@@ -324,10 +324,8 @@
         boolean mActive = true;
 
         protected TestListenerRegistration(Integer integer,
-                Consumer<TestListenerRegistration> consumer,
-                boolean outOfProcess) {
-            super(integer, CallerIdentity.forTest(Process.myUid(),
-                    Process.myPid() + (outOfProcess ? 1 : 0), "test", "test"), consumer);
+                Consumer<TestListenerRegistration> consumer) {
+            super(DIRECT_EXECUTOR, integer, consumer);
         }
     }
 
@@ -345,7 +343,7 @@
         }
 
         public void addListener(Integer request, Consumer<TestListenerRegistration> consumer) {
-            addRegistration(consumer, new TestListenerRegistration(request, consumer, true));
+            addRegistration(consumer, new TestListenerRegistration(request, consumer));
         }
 
         public void removeListener(Consumer<TestListenerRegistration> consumer) {
diff --git a/services/tests/servicestests/src/android/location/timezone/LocationTimeZoneEventTest.java b/services/tests/servicestests/src/android/location/timezone/LocationTimeZoneEventTest.java
index 80373ac..f9dd7dc 100644
--- a/services/tests/servicestests/src/android/location/timezone/LocationTimeZoneEventTest.java
+++ b/services/tests/servicestests/src/android/location/timezone/LocationTimeZoneEventTest.java
@@ -23,6 +23,8 @@
 
 import static java.util.Collections.singletonList;
 
+import android.os.UserHandle;
+
 import org.junit.Test;
 
 import java.util.List;
@@ -33,6 +35,10 @@
 
     private static final List<String> ARBITRARY_TIME_ZONE_IDS = singletonList("Europe/London");
 
+    private static final UserHandle ARBITRARY_USER_HANDLE = UserHandle.SYSTEM;
+    private static final UserHandle ARBITRARY_USER_HANDLE2 =
+            UserHandle.of(ARBITRARY_USER_HANDLE.getIdentifier() + 1);
+
     @Test(expected = RuntimeException.class)
     public void testSetInvalidEventType() {
         new LocationTimeZoneEvent.Builder().setEventType(Integer.MAX_VALUE);
@@ -41,6 +47,7 @@
     @Test(expected = RuntimeException.class)
     public void testBuildUnsetEventType() {
         new LocationTimeZoneEvent.Builder()
+                .setUserHandle(ARBITRARY_USER_HANDLE)
                 .setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS)
                 .setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS)
                 .build();
@@ -49,6 +56,7 @@
     @Test(expected = RuntimeException.class)
     public void testInvalidTimeZoneIds() {
         new LocationTimeZoneEvent.Builder()
+                .setUserHandle(ARBITRARY_USER_HANDLE)
                 .setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN)
                 .setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS)
                 .setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS)
@@ -58,6 +66,7 @@
     @Test
     public void testEquals() {
         LocationTimeZoneEvent.Builder builder1 = new LocationTimeZoneEvent.Builder()
+                .setUserHandle(ARBITRARY_USER_HANDLE)
                 .setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN)
                 .setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS);
         {
@@ -66,6 +75,7 @@
         }
 
         LocationTimeZoneEvent.Builder builder2 = new LocationTimeZoneEvent.Builder()
+                .setUserHandle(ARBITRARY_USER_HANDLE)
                 .setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN)
                 .setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS);
         {
@@ -75,6 +85,22 @@
             assertEquals(two, one);
         }
 
+        builder1.setUserHandle(ARBITRARY_USER_HANDLE2);
+        {
+            LocationTimeZoneEvent one = builder1.build();
+            LocationTimeZoneEvent two = builder2.build();
+            assertNotEquals(one, two);
+            assertNotEquals(two, one);
+        }
+
+        builder2.setUserHandle(ARBITRARY_USER_HANDLE2);
+        {
+            LocationTimeZoneEvent one = builder1.build();
+            LocationTimeZoneEvent two = builder2.build();
+            assertEquals(one, two);
+            assertEquals(two, one);
+        }
+
         builder1.setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS + 1);
         {
             LocationTimeZoneEvent one = builder1.build();
@@ -127,6 +153,7 @@
     @Test
     public void testParcelable() {
         LocationTimeZoneEvent.Builder builder = new LocationTimeZoneEvent.Builder()
+                .setUserHandle(ARBITRARY_USER_HANDLE)
                 .setEventType(LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE)
                 .setElapsedRealtimeNanos(ARBITRARY_ELAPSED_REALTIME_NANOS);
         assertRoundTripParcelable(builder.build());
diff --git a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
index b7a36f2..8d4f2aa 100644
--- a/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/VibratorServiceTest.java
@@ -20,12 +20,13 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalMatchers.gt;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.intThat;
-import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
@@ -167,7 +168,7 @@
     @Test
     public void createService_initializesNativeService() {
         createService();
-        verify(mNativeWrapperMock).vibratorInit();
+        verify(mNativeWrapperMock).vibratorInit(notNull());
         verify(mNativeWrapperMock).vibratorOff();
     }
 
@@ -294,7 +295,7 @@
         assertTrue(service.isVibrating());
 
         verify(mNativeWrapperMock).vibratorOff();
-        verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class));
+        verify(mNativeWrapperMock).vibratorOn(eq(100L), gt(0L));
         verify(mNativeWrapperMock).vibratorSetAmplitude(eq(128));
     }
 
@@ -307,7 +308,7 @@
         assertTrue(service.isVibrating());
 
         verify(mNativeWrapperMock).vibratorOff();
-        verify(mNativeWrapperMock).vibratorOn(eq(100L), any(VibratorService.Vibration.class));
+        verify(mNativeWrapperMock).vibratorOn(eq(100L), gt(0L));
         verify(mNativeWrapperMock, never()).vibratorSetAmplitude(anyInt());
     }
 
@@ -321,10 +322,8 @@
         vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
 
         verify(mNativeWrapperMock).vibratorOff();
-        verify(mNativeWrapperMock).vibratorPerformEffect(
-                eq((long) VibrationEffect.EFFECT_CLICK),
-                eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG),
-                any(VibratorService.Vibration.class));
+        verify(mNativeWrapperMock).vibratorPerformEffect(eq((long) VibrationEffect.EFFECT_CLICK),
+                eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), gt(0L));
     }
 
     @Test
@@ -343,7 +342,7 @@
 
         verify(mNativeWrapperMock).vibratorOff();
         verify(mNativeWrapperMock).vibratorPerformComposedEffect(
-                primitivesCaptor.capture(), any(VibratorService.Vibration.class));
+                primitivesCaptor.capture(), gt(0L));
 
         // Check all primitive effect fields are passed down to the HAL.
         assertEquals(1, primitivesCaptor.getValue().length);
@@ -368,7 +367,7 @@
 
         // Wait for VibrateThread to turn vibrator ON with total timing and no callback.
         Thread.sleep(5);
-        verify(mNativeWrapperMock).vibratorOn(eq(30L), isNull());
+        verify(mNativeWrapperMock).vibratorOn(eq(30L), eq(0L));
 
         // First amplitude set right away.
         verify(mNativeWrapperMock).vibratorSetAmplitude(eq(100));
@@ -384,11 +383,11 @@
 
     @Test
     public void vibrate_withOneShotAndNativeCallbackTriggered_finishesVibration() {
-        doAnswer(invocation -> {
-            ((VibratorService.Vibration) invocation.getArgument(1)).onComplete();
-            return null;
-        }).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class));
         VibratorService service = createService();
+        doAnswer(invocation -> {
+            service.onVibrationComplete(invocation.getArgument(1));
+            return null;
+        }).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong());
         Mockito.clearInvocations(mNativeWrapperMock);
 
         vibrate(service, VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
@@ -396,7 +395,7 @@
         InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
         inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
         inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(100L),
-                any(VibratorService.Vibration.class));
+                gt(0L));
         inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
     }
 
@@ -404,12 +403,11 @@
     public void vibrate_withPrebakedAndNativeCallbackTriggered_finishesVibration() {
         when(mNativeWrapperMock.vibratorGetSupportedEffects())
                 .thenReturn(new int[]{VibrationEffect.EFFECT_CLICK});
-        doAnswer(invocation -> {
-            ((VibratorService.Vibration) invocation.getArgument(2)).onComplete();
-            return 10_000L; // 10s
-        }).when(mNativeWrapperMock).vibratorPerformEffect(
-                anyLong(), anyLong(), any(VibratorService.Vibration.class));
         VibratorService service = createService();
+        doAnswer(invocation -> {
+            service.onVibrationComplete(invocation.getArgument(2));
+            return 10_000L; // 10s
+        }).when(mNativeWrapperMock).vibratorPerformEffect(anyLong(), anyLong(), anyLong());
         Mockito.clearInvocations(mNativeWrapperMock);
 
         vibrate(service, VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK));
@@ -419,7 +417,7 @@
         inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformEffect(
                 eq((long) VibrationEffect.EFFECT_CLICK),
                 eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG),
-                any(VibratorService.Vibration.class));
+                gt(0L));
         inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
     }
 
@@ -436,20 +434,19 @@
         Thread.sleep(15);
         InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
         inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
-        inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(3L), isNull());
-        inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(2L), isNull());
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(3L), eq(0L));
+        inOrderVerifier.verify(mNativeWrapperMock).vibratorOn(eq(2L), eq(0L));
         inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
     }
 
     @Test
     public void vibrate_withComposedAndNativeCallbackTriggered_finishesVibration() {
         mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
-        doAnswer(invocation -> {
-            ((VibratorService.Vibration) invocation.getArgument(1)).onComplete();
-            return null;
-        }).when(mNativeWrapperMock).vibratorPerformComposedEffect(
-                any(), any(VibratorService.Vibration.class));
         VibratorService service = createService();
+        doAnswer(invocation -> {
+            service.onVibrationComplete(invocation.getArgument(1));
+            return null;
+        }).when(mNativeWrapperMock).vibratorPerformComposedEffect(any(), anyLong());
         Mockito.clearInvocations(mNativeWrapperMock);
 
         VibrationEffect effect = VibrationEffect.startComposition()
@@ -460,32 +457,7 @@
         InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
         inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
         inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect(
-                any(VibrationEffect.Composition.PrimitiveEffect[].class),
-                any(VibratorService.Vibration.class));
-        inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
-    }
-
-    @Test
-    public void vibrate_whenBinderDies_cancelsVibration() {
-        mockVibratorCapabilities(IVibrator.CAP_COMPOSE_EFFECTS);
-        doAnswer(invocation -> {
-            ((VibratorService.Vibration) invocation.getArgument(1)).binderDied();
-            return null;
-        }).when(mNativeWrapperMock).vibratorPerformComposedEffect(
-                any(), any(VibratorService.Vibration.class));
-        VibratorService service = createService();
-        Mockito.clearInvocations(mNativeWrapperMock);
-
-        VibrationEffect effect = VibrationEffect.startComposition()
-                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 10)
-                .compose();
-        vibrate(service, effect);
-
-        InOrder inOrderVerifier = inOrder(mNativeWrapperMock);
-        inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
-        inOrderVerifier.verify(mNativeWrapperMock).vibratorPerformComposedEffect(
-                any(VibrationEffect.Composition.PrimitiveEffect[].class),
-                any(VibratorService.Vibration.class));
+                any(VibrationEffect.Composition.PrimitiveEffect[].class), gt(0L));
         inOrderVerifier.verify(mNativeWrapperMock).vibratorOff();
     }
 
@@ -513,12 +485,11 @@
 
     @Test
     public void registerVibratorStateListener_callbacksAreTriggered() throws Exception {
-        doAnswer(invocation -> {
-            ((VibratorService.Vibration) invocation.getArgument(1)).onComplete();
-            return null;
-        }).when(mNativeWrapperMock).vibratorOn(anyLong(), any(VibratorService.Vibration.class));
         VibratorService service = createService();
-
+        doAnswer(invocation -> {
+            service.onVibrationComplete(invocation.getArgument(1));
+            return null;
+        }).when(mNativeWrapperMock).vibratorOn(anyLong(), anyLong());
         service.registerVibratorStateListener(mVibratorStateListenerMock);
         verify(mVibratorStateListenerMock).onVibrating(false);
         Mockito.clearInvocations(mVibratorStateListenerMock);
@@ -569,15 +540,15 @@
 
         verify(mNativeWrapperMock).vibratorPerformEffect(
                 eq((long) VibrationEffect.EFFECT_CLICK),
-                eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), any());
+                eq((long) VibrationEffect.EFFECT_STRENGTH_STRONG), anyLong());
         verify(mNativeWrapperMock).vibratorPerformEffect(
                 eq((long) VibrationEffect.EFFECT_TICK),
-                eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), any());
+                eq((long) VibrationEffect.EFFECT_STRENGTH_MEDIUM), anyLong());
         verify(mNativeWrapperMock).vibratorPerformEffect(
                 eq((long) VibrationEffect.EFFECT_DOUBLE_CLICK),
-                eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), any());
+                eq((long) VibrationEffect.EFFECT_STRENGTH_LIGHT), anyLong());
         verify(mNativeWrapperMock, never()).vibratorPerformEffect(
-                eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), any());
+                eq((long) VibrationEffect.EFFECT_HEAVY_CLICK), anyLong(), anyLong());
     }
 
     @Test
@@ -644,7 +615,7 @@
 
         // Ringtone vibration is off, so only the other 3 are propagated to native.
         verify(mNativeWrapperMock, times(3)).vibratorPerformComposedEffect(
-                primitivesCaptor.capture(), any());
+                primitivesCaptor.capture(), anyLong());
 
         List<VibrationEffect.Composition.PrimitiveEffect[]> values =
                 primitivesCaptor.getAllValues();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 763654d..dda81ff 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -187,7 +187,7 @@
         moveEachPointers(mLastEvent, p(10, 10), p(10, 10));
         send(mLastEvent);
         goToStateClearFrom(STATE_DRAGGING_2FINGERS);
-        assertCapturedEvents(ACTION_DOWN, ACTION_MOVE, ACTION_MOVE, ACTION_UP);
+        assertCapturedEvents(ACTION_DOWN, ACTION_MOVE, ACTION_MOVE, ACTION_MOVE, ACTION_UP);
     }
 
     @Test
@@ -288,7 +288,7 @@
         assertState(STATE_DRAGGING);
         goToStateClearFrom(STATE_DRAGGING_2FINGERS);
         assertState(STATE_CLEAR);
-        assertCapturedEvents(ACTION_DOWN, ACTION_UP);
+        assertCapturedEvents(ACTION_DOWN, ACTION_MOVE, ACTION_UP);
         assertCapturedEventsNoHistory();
     }
 
@@ -301,6 +301,7 @@
         assertState(STATE_CLEAR);
         assertCapturedEvents(
                 /* goto dragging state */ ACTION_DOWN,
+                ACTION_MOVE,
                 /* leave dragging state */ ACTION_UP,
                 ACTION_DOWN,
                 ACTION_POINTER_DOWN,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
index bec9f26..a10e0ba 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationGestureHandlerTest.java
@@ -81,7 +81,7 @@
 
     @After
     public void tearDown() {
-        mWindowMagnificationManager.disableWindowMagnifier(DISPLAY_0, true);
+        mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, true);
     }
 
     @Test
@@ -225,7 +225,7 @@
             }
             break;
             case STATE_SHOW_MAGNIFIER: {
-                mWindowMagnificationManager.disableWindowMagnifier(DISPLAY_0, false);
+                mWindowMagnificationManager.disableWindowMagnification(DISPLAY_0, false);
             }
             break;
             case STATE_TWO_FINGERS_DOWN: {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index 70e6a34..e067b7e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -68,8 +68,10 @@
     private static final int CURRENT_USER_ID = UserHandle.USER_CURRENT;
 
     private MockWindowMagnificationConnection mMockConnection;
-    @Mock private Context mContext;
-    @Mock private StatusBarManagerInternal mMockStatusBarManagerInternal;
+    @Mock
+    private Context mContext;
+    @Mock
+    private StatusBarManagerInternal mMockStatusBarManagerInternal;
     private MockContentResolver mResolver;
     private WindowMagnificationManager mWindowMagnificationManager;
 
@@ -84,7 +86,7 @@
 
         when(mContext.getContentResolver()).thenReturn(mResolver);
         doAnswer((InvocationOnMock invocation) -> {
-            final boolean connect =  (Boolean) invocation.getArguments()[0];
+            final boolean connect = (Boolean) invocation.getArguments()[0];
             mWindowMagnificationManager.setConnection(
                     connect ? mMockConnection.getConnection() : null);
             return null;
@@ -161,7 +163,7 @@
     public void enable_TestDisplay_enableWindowMagnification() throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
 
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2f, 200f, 300f);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, 200f, 300f);
 
         verify(mMockConnection.getConnection()).enableWindowMagnification(TEST_DISPLAY, 2f,
                 200f, 300f);
@@ -170,9 +172,9 @@
     @Test
     public void disable_testDisplay_disableWindowMagnification() throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
 
-        mWindowMagnificationManager.disableWindowMagnifier(TEST_DISPLAY, false);
+        mWindowMagnificationManager.disableWindowMagnification(TEST_DISPLAY, false);
 
         verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY);
     }
@@ -183,7 +185,7 @@
 
         assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
 
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
 
         assertTrue(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
     }
@@ -198,7 +200,7 @@
     @Test
     public void persistScale_setValue_expectedValueInProvider() {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2.0f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
         mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f);
 
         mWindowMagnificationManager.persistScale(TEST_DISPLAY);
@@ -211,7 +213,7 @@
     @Test
     public void scaleSetterGetter_enabledOnTestDisplay_expectedValue() {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2.0f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.0f, NaN, NaN);
 
         mWindowMagnificationManager.setScale(TEST_DISPLAY, 2.5f);
 
@@ -221,7 +223,7 @@
     @Test
     public void scaleSetterGetter_scaleIsOutOfRang_getNormalizeValue() {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2.5f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
 
         mWindowMagnificationManager.setScale(TEST_DISPLAY, 10.0f);
 
@@ -232,9 +234,9 @@
     @Test
     public void moveWindowMagnifier() throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 2f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2f, NaN, NaN);
 
-        mWindowMagnificationManager.moveWindowMagnifier(TEST_DISPLAY, 200, 300);
+        mWindowMagnificationManager.moveWindowMagnification(TEST_DISPLAY, 200, 300);
         verify(mMockConnection.getConnection()).moveWindowMagnifier(TEST_DISPLAY, 200, 300);
     }
 
@@ -254,7 +256,7 @@
     @Test
     public void pointersInWindow_returnCorrectValue() throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3.0f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3.0f, NaN, NaN);
         mMockConnection.getConnectionCallback().onWindowMagnifierBoundsChanged(TEST_DISPLAY,
                 new Rect(0, 0, 500, 500));
         PointF[] pointersLocation = new PointF[2];
@@ -268,7 +270,7 @@
     @Test
     public void binderDied_windowMagnifierIsEnabled_resetState() throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
 
         mMockConnection.getDeathRecipient().binderDied();
 
@@ -280,7 +282,7 @@
             requestConnectionToNull_disableAllMagnifiersAndRequestWindowMagnificationConnection()
             throws RemoteException {
         mWindowMagnificationManager.setConnection(mMockConnection.getConnection());
-        mWindowMagnificationManager.enableWindowMagnifier(TEST_DISPLAY, 3f, NaN, NaN);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 3f, NaN, NaN);
 
         assertTrue(mWindowMagnificationManager.requestConnection(false));
 
@@ -306,21 +308,24 @@
     @Test
     public void requestConnection_registerAndUnregisterBroadcastReceiver() {
         assertTrue(mWindowMagnificationManager.requestConnection(true));
-        verify(mContext).registerReceiver(any(BroadcastReceiver.class),  any(IntentFilter.class));
+        verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
 
         assertTrue(mWindowMagnificationManager.requestConnection(false));
         verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
     }
 
     @Test
-    public void onReceiveScreenOff_removeMagnificationButtonAndDisableWindowMagnification()
+    public void onScreenOff_windowMagnifierIsEnabled_removeButtonAndDisableWindowMagnification()
             throws RemoteException {
         mWindowMagnificationManager.requestConnection(true);
+        mWindowMagnificationManager.enableWindowMagnification(TEST_DISPLAY, 2.5f, NaN, NaN);
+
         mWindowMagnificationManager.mScreenStateReceiver.onReceive(mContext,
                 new Intent(Intent.ACTION_SCREEN_OFF));
 
         verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
         verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY);
+        assertFalse(mWindowMagnificationManager.isWindowMagnifierEnabled(TEST_DISPLAY));
     }
 
     private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 32afe82..e6fc792 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -4465,6 +4465,7 @@
         final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 19436);
         addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
         mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
+        mServiceContext.permissions.add(permission.INTERACT_ACROSS_USERS_FULL);
 
         // Even if the caller is the managed profile, the current user is the user 0
         when(getServices().iactivityManager.getCurrentUser())
@@ -5694,6 +5695,7 @@
 
         final long ident = mServiceContext.binder.clearCallingIdentity();
         configureContextForAccess(mServiceContext, true);
+        mServiceContext.permissions.add(permission.MARK_DEVICE_ORGANIZATION_OWNED);
 
         mServiceContext.binder.callingUid =
                 UserHandle.getUid(CALLER_USER_HANDLE,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index ce7ac9e..09b6d7b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -259,18 +259,7 @@
 
     @Override
     public int checkPermission(String permission, int pid, int uid) {
-        if (UserHandle.isSameApp(binder.getCallingUid(), SYSTEM_UID)) {
-            return PackageManager.PERMISSION_GRANTED; // Assume system has all permissions.
-        }
-        List<String> permissions = binder.callingPermissions.get(binder.getCallingUid());
-        if (permissions == null) {
-            permissions = callerPermissions;
-        }
-        if (permissions.contains(permission)) {
-            return PackageManager.PERMISSION_GRANTED;
-        } else {
-            return PackageManager.PERMISSION_DENIED;
-        }
+        return checkPermission(permission);
     }
 
     @Override
@@ -480,11 +469,32 @@
 
     @Override
     public int checkCallingPermission(String permission) {
-        return spiedContext.checkCallingPermission(permission);
+        return checkPermission(permission);
+    }
+
+    @Override
+    public int checkCallingOrSelfPermission(String permission) {
+        return checkPermission(permission);
     }
 
     @Override
     public void startActivityAsUser(Intent intent, UserHandle userHandle) {
         spiedContext.startActivityAsUser(intent, userHandle);
     }
+
+    private int checkPermission(String permission) {
+        if (UserHandle.isSameApp(binder.getCallingUid(), SYSTEM_UID)) {
+            return PackageManager.PERMISSION_GRANTED; // Assume system has all permissions.
+        }
+        List<String> permissions = binder.callingPermissions.get(binder.getCallingUid());
+        if (permissions == null) {
+            permissions = callerPermissions;
+        }
+        if (permissions.contains(permission)) {
+            return PackageManager.PERMISSION_GRANTED;
+        } else {
+            return PackageManager.PERMISSION_DENIED;
+        }
+    }
+
 }
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
index ef2365e..3f324a2 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDevicePlaybackTest.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.hdmi;
 
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
 import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
 import static com.android.server.hdmi.Constants.ADDR_TV;
 import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_ENABLE_CEC;
@@ -693,6 +694,82 @@
     }
 
     @Test
+    public void sendVolumeKeyEvent_toTv_activeSource() {
+        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setSystemAudioActivated(false);
+        mHdmiControlService.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
+
+        HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed(
+                mPlaybackLogicalAddress, ADDR_TV, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
+        HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased(
+                mPlaybackLogicalAddress, ADDR_TV);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isTrue();
+        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+    }
+
+    @Test
+    public void sendVolumeKeyEvent_toAudio_activeSource() {
+        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setSystemAudioActivated(true);
+        mHdmiControlService.setActiveSource(mPlaybackLogicalAddress, mPlaybackPhysicalAddress);
+
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
+
+        HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed(
+                mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
+        HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased(
+                mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isTrue();
+        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+    }
+
+    @Test
+    public void sendVolumeKeyEvent_toTv_inactiveSource() {
+        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setSystemAudioActivated(false);
+        mHdmiControlService.setActiveSource(ADDR_TV, 0x0000);
+
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
+
+        HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed(
+                mPlaybackLogicalAddress, ADDR_TV, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
+        HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased(
+                mPlaybackLogicalAddress, ADDR_TV);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse();
+        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+    }
+
+    @Test
+    public void sendVolumeKeyEvent_toAudio_inactiveSource() {
+        mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
+        mHdmiControlService.setSystemAudioActivated(true);
+        mHdmiControlService.setActiveSource(ADDR_TV, 0x0000);
+
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, true);
+        mHdmiCecLocalDevicePlayback.sendVolumeKeyEvent(KeyEvent.KEYCODE_VOLUME_UP, false);
+
+        HdmiCecMessage pressed = HdmiCecMessageBuilder.buildUserControlPressed(
+                mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM, HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP);
+        HdmiCecMessage released = HdmiCecMessageBuilder.buildUserControlReleased(
+                mPlaybackLogicalAddress, ADDR_AUDIO_SYSTEM);
+        mTestLooper.dispatchAll();
+
+        assertThat(mHdmiCecLocalDevicePlayback.mIsActiveSource).isFalse();
+        assertThat(mNativeWrapper.getResultMessages()).containsAllOf(pressed, released);
+    }
+
+    @Test
     public void handleSetStreamPath_broadcastsActiveSource() {
         HdmiCecMessage setStreamPath = HdmiCecMessageBuilder.buildSetStreamPath(ADDR_TV,
                 mPlaybackPhysicalAddress);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
index 53c4d6f..f17173f 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
@@ -62,6 +62,32 @@
         assertThat(message).isEqualTo(buildMessage("5F:81:21:00"));
     }
 
+    @Test
+    public void buildSetOsdName_short() {
+        String deviceName = "abc";
+        HdmiCecMessage message = HdmiCecMessageBuilder.buildSetOsdNameCommand(ADDR_PLAYBACK_1,
+                ADDR_TV, deviceName);
+        assertThat(message).isEqualTo(buildMessage("40:47:61:62:63"));
+    }
+
+    @Test
+    public void buildSetOsdName_maximumLength() {
+        String deviceName = "abcdefghijklmn";
+        HdmiCecMessage message = HdmiCecMessageBuilder.buildSetOsdNameCommand(ADDR_PLAYBACK_1,
+                ADDR_TV, deviceName);
+        assertThat(message).isEqualTo(
+                buildMessage("40:47:61:62:63:64:65:66:67:68:69:6A:6B:6C:6D:6E"));
+    }
+
+    @Test
+    public void buildSetOsdName_tooLong() {
+        String deviceName = "abcdefghijklmnop";
+        HdmiCecMessage message = HdmiCecMessageBuilder.buildSetOsdNameCommand(ADDR_PLAYBACK_1,
+                ADDR_TV, deviceName);
+        assertThat(message).isEqualTo(
+                buildMessage("40:47:61:62:63:64:65:66:67:68:69:6A:6B:6C:6D:6E"));
+    }
+
     /**
      * Build a CEC message from a hex byte string with bytes separated by {@code :}.
      *
diff --git a/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java b/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java
index 0219f22..4c36747 100644
--- a/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java
@@ -15,13 +15,15 @@
  */
 package com.android.server.job;
 
-import android.util.KeyValueListParser;
+import android.annotation.Nullable;
+import android.provider.DeviceConfig;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.job.JobSchedulerService.MaxJobCounts;
 
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -29,19 +31,32 @@
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class MaxJobCountsTest {
+    @After
+    public void tearDown() throws Exception {
+        resetConfig();
+    }
 
-    private void check(String config,
+    private void resetConfig() {
+        // DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually.
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "total", "", false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "maxbg", "", false);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "minbg", "", false);
+    }
+
+    private void check(@Nullable DeviceConfig.Properties config,
             int defaultTotal, int defaultMaxBg, int defaultMinBg,
-            int expectedTotal, int expectedMaxBg, int expectedMinBg) {
-        final KeyValueListParser parser = new KeyValueListParser(',');
-        parser.setString(config);
+            int expectedTotal, int expectedMaxBg, int expectedMinBg) throws Exception {
+        resetConfig();
+        if (config != null) {
+            DeviceConfig.setProperties(config);
+        }
 
         final MaxJobCounts counts = new JobSchedulerService.MaxJobCounts(
                 defaultTotal, "total",
                 defaultMaxBg, "maxbg",
                 defaultMinBg, "minbg");
 
-        counts.parse(parser);
+        counts.update();
 
         Assert.assertEquals(expectedTotal, counts.getMaxTotal());
         Assert.assertEquals(expectedMaxBg, counts.getMaxBg());
@@ -49,24 +64,35 @@
     }
 
     @Test
-    public void test() {
+    public void test() throws Exception {
         // Tests with various combinations.
-        check("", /*default*/ 5, 1, 0, /*expected*/ 5, 1, 0);
-        check("", /*default*/ 5, 0, 0, /*expected*/ 5, 1, 0);
-        check("", /*default*/ 0, 0, 0, /*expected*/ 1, 1, 0);
-        check("", /*default*/ -1, -1, -1, /*expected*/ 1, 1, 0);
-        check("", /*default*/ 5, 5, 5, /*expected*/ 5, 5, 4);
-        check("", /*default*/ 6, 5, 6, /*expected*/ 6, 5, 5);
-        check("", /*default*/ 4, 5, 6, /*expected*/ 4, 4, 3);
-        check("", /*default*/ 5, 1, 1, /*expected*/ 5, 1, 1);
-        check("", /*default*/ 15, 15, 15, /*expected*/ 15, 15, 14);
-        check("", /*default*/ 16, 16, 16, /*expected*/ 16, 16, 15);
-        check("", /*default*/ 20, 20, 20, /*expected*/ 16, 16, 15);
+        check(null, /*default*/ 5, 1, 0, /*expected*/ 5, 1, 0);
+        check(null, /*default*/ 5, 0, 0, /*expected*/ 5, 1, 0);
+        check(null, /*default*/ 0, 0, 0, /*expected*/ 1, 1, 0);
+        check(null, /*default*/ -1, -1, -1, /*expected*/ 1, 1, 0);
+        check(null, /*default*/ 5, 5, 5, /*expected*/ 5, 5, 4);
+        check(null, /*default*/ 6, 5, 6, /*expected*/ 6, 5, 5);
+        check(null, /*default*/ 4, 5, 6, /*expected*/ 4, 4, 3);
+        check(null, /*default*/ 5, 1, 1, /*expected*/ 5, 1, 1);
+        check(null, /*default*/ 15, 15, 15, /*expected*/ 15, 15, 14);
+        check(null, /*default*/ 16, 16, 16, /*expected*/ 16, 16, 15);
+        check(null, /*default*/ 20, 20, 20, /*expected*/ 16, 16, 15);
 
         // Test for overriding with a setting string.
-        check("total=5,maxbg=4,minbg=3", /*default*/ 9, 9, 9, /*expected*/ 5, 4, 3);
-        check("total=5", /*default*/ 9, 9, 9, /*expected*/ 5, 5, 4);
-        check("maxbg=4", /*default*/ 9, 9, 9, /*expected*/ 9, 4, 4);
-        check("minbg=3", /*default*/ 9, 9, 9, /*expected*/ 9, 9, 3);
+        check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+                        .setInt("total", 5)
+                        .setInt("maxbg", 4)
+                        .setInt("minbg", 3)
+                        .build(),
+                /*default*/ 9, 9, 9, /*expected*/ 5, 4, 3);
+        check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+                        .setInt("total", 5).build(),
+                /*default*/ 9, 9, 9, /*expected*/ 5, 5, 4);
+        check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+                        .setInt("maxbg", 4).build(),
+                /*default*/ 9, 9, 9, /*expected*/ 9, 4, 4);
+        check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
+                        .setInt("minbg", 3).build(),
+                /*default*/ 9, 9, 9, /*expected*/ 9, 9, 3);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
new file mode 100644
index 0000000..d7ed96f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/ConfigurationInternalTest.java
@@ -0,0 +1,182 @@
+/*
+ * 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.timezonedetector;
+
+import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED;
+import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
+import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED;
+import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.timezonedetector.TimeZoneCapabilities;
+
+import org.junit.Test;
+
+/**
+ * Tests for {@link ConfigurationInternal} and the {@link TimeZoneCapabilities} and
+ * {@link android.app.timezonedetector.TimeZoneConfiguration} that can be generated from it.
+ */
+public class ConfigurationInternalTest {
+
+    private static final int ARBITRARY_USER_ID = 99999;
+
+    /**
+     * Tests when {@link ConfigurationInternal#isUserConfigAllowed()} and
+     * {@link ConfigurationInternal#isAutoDetectionSupported()} are both true.
+     */
+    @Test
+    public void test_unrestricted() {
+        ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+                .setUserConfigAllowed(true)
+                .setAutoDetectionSupported(true)
+                .setAutoDetectionEnabled(true)
+                .setLocationEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        {
+            ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(true)
+                    .build();
+            assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOnConfig.getGeoDetectionEnabledSetting());
+            assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior());
+            assertTrue(autoOnConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilities capabilities = autoOnConfig.createCapabilities();
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled());
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled());
+            assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSuggestManualTimeZone());
+            assertEquals(autoOnConfig.asConfiguration(), capabilities.getConfiguration());
+            assertTrue(capabilities.getConfiguration().isAutoDetectionEnabled());
+            assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled());
+        }
+
+        {
+            ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(false)
+                    .build();
+            assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOffConfig.getGeoDetectionEnabledSetting());
+            assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior());
+            assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilities capabilities = autoOffConfig.createCapabilities();
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled());
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled());
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
+            assertEquals(autoOffConfig.asConfiguration(), capabilities.getConfiguration());
+            assertFalse(capabilities.getConfiguration().isAutoDetectionEnabled());
+            assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled());
+        }
+    }
+
+    /** Tests when {@link ConfigurationInternal#isUserConfigAllowed()} is false */
+    @Test
+    public void test_restricted() {
+        ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+                .setUserConfigAllowed(false)
+                .setAutoDetectionSupported(true)
+                .setAutoDetectionEnabled(true)
+                .setLocationEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        {
+            ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(true)
+                    .build();
+            assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOnConfig.getGeoDetectionEnabledSetting());
+            assertTrue(autoOnConfig.getAutoDetectionEnabledBehavior());
+            assertTrue(autoOnConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilities capabilities = autoOnConfig.createCapabilities();
+            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled());
+            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled());
+            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone());
+            assertEquals(autoOnConfig.asConfiguration(), capabilities.getConfiguration());
+            assertTrue(capabilities.getConfiguration().isAutoDetectionEnabled());
+            assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled());
+        }
+
+        {
+            ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(false)
+                    .build();
+            assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOffConfig.getGeoDetectionEnabledSetting());
+            assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior());
+            assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilities capabilities = autoOffConfig.createCapabilities();
+            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled());
+            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled());
+            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone());
+            assertEquals(autoOffConfig.asConfiguration(), capabilities.getConfiguration());
+            assertFalse(capabilities.getConfiguration().isAutoDetectionEnabled());
+            assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled());
+        }
+    }
+
+    /** Tests when {@link ConfigurationInternal#isAutoDetectionSupported()} is false. */
+    @Test
+    public void test_autoDetectNotSupported() {
+        ConfigurationInternal baseConfig = new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+                .setUserConfigAllowed(true)
+                .setAutoDetectionSupported(false)
+                .setAutoDetectionEnabled(true)
+                .setLocationEnabled(true)
+                .setGeoDetectionEnabled(true)
+                .build();
+        {
+            ConfigurationInternal autoOnConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(true)
+                    .build();
+            assertTrue(autoOnConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOnConfig.getGeoDetectionEnabledSetting());
+            assertFalse(autoOnConfig.getAutoDetectionEnabledBehavior());
+            assertFalse(autoOnConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilities capabilities = autoOnConfig.createCapabilities();
+            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled());
+            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled());
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
+            assertEquals(autoOnConfig.asConfiguration(), capabilities.getConfiguration());
+            assertTrue(capabilities.getConfiguration().isAutoDetectionEnabled());
+            assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled());
+        }
+        {
+            ConfigurationInternal autoOffConfig = new ConfigurationInternal.Builder(baseConfig)
+                    .setAutoDetectionEnabled(false)
+                    .build();
+            assertFalse(autoOffConfig.getAutoDetectionEnabledSetting());
+            assertTrue(autoOffConfig.getGeoDetectionEnabledSetting());
+            assertFalse(autoOffConfig.getAutoDetectionEnabledBehavior());
+            assertFalse(autoOffConfig.getGeoDetectionEnabledBehavior());
+
+            TimeZoneCapabilities capabilities = autoOffConfig.createCapabilities();
+            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled());
+            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled());
+            assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
+            assertEquals(autoOffConfig.asConfiguration(), capabilities.getConfiguration());
+            assertFalse(capabilities.getConfiguration().isAutoDetectionEnabled());
+            assertTrue(capabilities.getConfiguration().isGeoDetectionEnabled());
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
index e5e9311..4ef2082 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java
@@ -18,6 +18,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
@@ -32,56 +33,64 @@
 
 class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy {
 
-    private StrategyListener mListener;
+    private ConfigurationChangeListener mConfigurationChangeListener;
 
     // Fake state
-    private TimeZoneCapabilities mCapabilities;
-    private TimeZoneConfiguration mConfiguration;
+    private ConfigurationInternal mConfigurationInternal;
 
     // Call tracking.
     private GeolocationTimeZoneSuggestion mLastGeolocationSuggestion;
     private ManualTimeZoneSuggestion mLastManualSuggestion;
     private TelephonyTimeZoneSuggestion mLastTelephonySuggestion;
-    private boolean mHandleAutoTimeZoneConfigChangedCalled;
     private boolean mDumpCalled;
     private final List<Dumpable> mDumpables = new ArrayList<>();
 
     @Override
-    public void setStrategyListener(@NonNull StrategyListener listener) {
-        mListener = listener;
+    public void addConfigChangeListener(@NonNull ConfigurationChangeListener listener) {
+        if (mConfigurationChangeListener != null) {
+            fail("Fake only supports one listener");
+        }
+        mConfigurationChangeListener = listener;
     }
 
     @Override
-    public TimeZoneCapabilities getCapabilities(@UserIdInt int userId) {
-        return mCapabilities;
+    public ConfigurationInternal getConfigurationInternal(int userId) {
+        if (mConfigurationInternal.getUserId() != userId) {
+            fail("Fake only supports one user");
+        }
+        return mConfigurationInternal;
     }
 
     @Override
-    public boolean updateConfiguration(
-            @UserIdInt int userId, @NonNull TimeZoneConfiguration configuration) {
-        assertNotNull(mConfiguration);
-        assertNotNull(configuration);
+    public ConfigurationInternal getCurrentUserConfigurationInternal() {
+        return mConfigurationInternal;
+    }
 
-        // Simulate the strategy's behavior: the new configuration will be the old configuration
-        // merged with the new.
-        TimeZoneConfiguration oldConfiguration = mConfiguration;
-        TimeZoneConfiguration newConfiguration =
-                new TimeZoneConfiguration.Builder(mConfiguration)
-                        .mergeProperties(configuration)
-                        .build();
+    @Override
+    public boolean updateConfiguration(@NonNull TimeZoneConfiguration requestedChanges) {
+        assertNotNull(mConfigurationInternal);
+        assertNotNull(requestedChanges);
 
-        if (newConfiguration.equals(oldConfiguration)) {
+        // Simulate the real strategy's behavior: the new configuration will be updated to be the
+        // old configuration merged with the new if the user has the capability to up the settings.
+        // Then, if the configuration changed, the change listener is invoked.
+        TimeZoneCapabilities capabilities = mConfigurationInternal.createCapabilities();
+        TimeZoneConfiguration newConfiguration = capabilities.applyUpdate(requestedChanges);
+        if (newConfiguration == null) {
             return false;
         }
-        mConfiguration = newConfiguration;
-        mListener.onConfigurationChanged();
+
+        if (!newConfiguration.equals(capabilities.getConfiguration())) {
+            mConfigurationInternal = mConfigurationInternal.merge(newConfiguration);
+
+            // Note: Unlike the real strategy, the listeners is invoked synchronously.
+            mConfigurationChangeListener.onChange();
+        }
         return true;
     }
 
-    @Override
-    @NonNull
-    public TimeZoneConfiguration getConfiguration(@UserIdInt int userId) {
-        return mConfiguration;
+    public void simulateConfigurationChangeForTests() {
+        mConfigurationChangeListener.onChange();
     }
 
     @Override
@@ -103,11 +112,6 @@
     }
 
     @Override
-    public void handleAutoTimeZoneConfigChanged() {
-        mHandleAutoTimeZoneConfigChangedCalled = true;
-    }
-
-    @Override
     public void addDumpable(Dumpable dumpable) {
         mDumpables.add(dumpable);
     }
@@ -117,19 +121,14 @@
         mDumpCalled = true;
     }
 
-    void initializeConfiguration(TimeZoneConfiguration configuration) {
-        mConfiguration = configuration;
-    }
-
-    void initializeCapabilities(TimeZoneCapabilities capabilities) {
-        mCapabilities = capabilities;
+    void initializeConfiguration(ConfigurationInternal configurationInternal) {
+        mConfigurationInternal = configurationInternal;
     }
 
     void resetCallTracking() {
         mLastGeolocationSuggestion = null;
         mLastManualSuggestion = null;
         mLastTelephonySuggestion = null;
-        mHandleAutoTimeZoneConfigChangedCalled = false;
         mDumpCalled = false;
     }
 
@@ -146,10 +145,6 @@
         assertEquals(expectedSuggestion, mLastTelephonySuggestion);
     }
 
-    void verifyHandleAutoTimeZoneConfigChangedCalled() {
-        assertTrue(mHandleAutoTimeZoneConfigChangedCalled);
-    }
-
     void verifyDumpCalled() {
         assertTrue(mDumpCalled);
     }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java
new file mode 100644
index 0000000..f45b3a8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TestCallerIdentityInjector.java
@@ -0,0 +1,53 @@
+/*
+ * 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.timezonedetector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.annotation.UserIdInt;
+
+/** A fake {@link CallerIdentityInjector} used in tests. */
+public class TestCallerIdentityInjector implements CallerIdentityInjector {
+
+    private long mToken = 9999L;
+    private int mCallingUserId;
+    private Integer mCurrentCallingUserId;
+
+    public void initializeCallingUserId(@UserIdInt int userId) {
+        mCallingUserId = userId;
+        mCurrentCallingUserId = userId;
+    }
+
+    @Override
+    public int getCallingUserId() {
+        assertNotNull("callingUserId has been cleared", mCurrentCallingUserId);
+        return mCurrentCallingUserId;
+    }
+
+    @Override
+    public long clearCallingIdentity() {
+        mCurrentCallingUserId = null;
+        return mToken;
+    }
+
+    @Override
+    public void restoreCallingIdentity(long token) {
+        assertEquals(token, mToken);
+        mCurrentCallingUserId = mCallingUserId;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
index e9d57e5..918babc 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorInternalImplTest.java
@@ -16,6 +16,7 @@
 
 package com.android.server.timezonedetector;
 
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 
 import android.content.Context;
@@ -85,6 +86,18 @@
         mFakeTimeZoneDetectorStrategy.verifyHasDumpable(stubbedDumpable);
     }
 
+    @Test
+    public void testAddConfigurationListener() throws Exception {
+        boolean[] changeCalled = new boolean[2];
+        mTimeZoneDetectorInternal.addConfigurationListener(() -> changeCalled[0] = true);
+        mTimeZoneDetectorInternal.addConfigurationListener(() -> changeCalled[1] = true);
+
+        mFakeTimeZoneDetectorStrategy.simulateConfigurationChangeForTests();
+
+        assertTrue(changeCalled[0]);
+        assertTrue(changeCalled[1]);
+    }
+
     private static GeolocationTimeZoneSuggestion createGeolocationTimeZoneSuggestion() {
         return new GeolocationTimeZoneSuggestion(ARBITRARY_ZONE_IDS);
     }
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index 3a1ec4f..27b04b6 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -16,8 +16,6 @@
 
 package com.android.server.timezonedetector;
 
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
@@ -36,7 +34,6 @@
 import android.app.timezonedetector.ITimeZoneConfigurationListener;
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
-import android.app.timezonedetector.TimeZoneCapabilities;
 import android.app.timezonedetector.TimeZoneConfiguration;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -65,6 +62,7 @@
     private TimeZoneDetectorService mTimeZoneDetectorService;
     private HandlerThread mHandlerThread;
     private TestHandler mTestHandler;
+    private TestCallerIdentityInjector mTestCallerIdentityInjector;
 
 
     @Before
@@ -76,10 +74,14 @@
         mHandlerThread.start();
         mTestHandler = new TestHandler(mHandlerThread.getLooper());
 
+        mTestCallerIdentityInjector = new TestCallerIdentityInjector();
+        mTestCallerIdentityInjector.initializeCallingUserId(ARBITRARY_USER_ID);
+
         mFakeTimeZoneDetectorStrategy = new FakeTimeZoneDetectorStrategy();
 
         mTimeZoneDetectorService = new TimeZoneDetectorService(
-                mMockContext, mTestHandler, mFakeTimeZoneDetectorStrategy);
+                mMockContext, mTestHandler, mTestCallerIdentityInjector,
+                mFakeTimeZoneDetectorStrategy);
     }
 
     @After
@@ -107,40 +109,12 @@
     public void testGetCapabilities() {
         doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
 
-        TimeZoneCapabilities capabilities = createTimeZoneCapabilities();
-        mFakeTimeZoneDetectorStrategy.initializeCapabilities(capabilities);
-
-        assertEquals(capabilities, mTimeZoneDetectorService.getCapabilities());
-
-        verify(mMockContext).enforceCallingPermission(
-                eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
-                anyString());
-    }
-
-    @Test(expected = SecurityException.class)
-    public void testGetConfiguration_withoutPermission() {
-        doThrow(new SecurityException("Mock"))
-                .when(mMockContext).enforceCallingPermission(anyString(), any());
-
-        try {
-            mTimeZoneDetectorService.getConfiguration();
-            fail();
-        } finally {
-            verify(mMockContext).enforceCallingPermission(
-                    eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
-                    anyString());
-        }
-    }
-
-    @Test
-    public void testGetConfiguration() {
-        doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
-
-        TimeZoneConfiguration configuration =
-                createTimeZoneConfiguration(false /* autoDetectionEnabled */);
+        ConfigurationInternal configuration =
+                createConfigurationInternal(true /* autoDetectionEnabled*/);
         mFakeTimeZoneDetectorStrategy.initializeConfiguration(configuration);
 
-        assertEquals(configuration, mTimeZoneDetectorService.getConfiguration());
+        assertEquals(configuration.createCapabilities(),
+                mTimeZoneDetectorService.getCapabilities());
 
         verify(mMockContext).enforceCallingPermission(
                 eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
@@ -181,10 +155,9 @@
 
     @Test
     public void testConfigurationChangeListenerRegistrationAndCallbacks() throws Exception {
-        TimeZoneConfiguration autoDetectDisabledConfiguration =
-                createTimeZoneConfiguration(false /* autoDetectionEnabled */);
-
-        mFakeTimeZoneDetectorStrategy.initializeConfiguration(autoDetectDisabledConfiguration);
+        ConfigurationInternal initialConfiguration =
+                createConfigurationInternal(false /* autoDetectionEnabled */);
+        mFakeTimeZoneDetectorStrategy.initializeConfiguration(initialConfiguration);
 
         IBinder mockListenerBinder = mock(IBinder.class);
         ITimeZoneConfigurationListener mockListener = mock(ITimeZoneConfigurationListener.class);
@@ -210,13 +183,12 @@
             // Simulate the configuration being changed and verify the mockListener was notified.
             TimeZoneConfiguration autoDetectEnabledConfiguration =
                     createTimeZoneConfiguration(true /* autoDetectionEnabled */);
-
             mTimeZoneDetectorService.updateConfiguration(autoDetectEnabledConfiguration);
 
             verify(mMockContext).enforceCallingPermission(
                     eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
                     anyString());
-            verify(mockListener).onChange(autoDetectEnabledConfiguration);
+            verify(mockListener).onChange();
             verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
             reset(mockListenerBinder, mockListener, mMockContext);
         }
@@ -242,12 +214,14 @@
         {
             doNothing().when(mMockContext).enforceCallingPermission(anyString(), any());
 
+            TimeZoneConfiguration autoDetectDisabledConfiguration =
+                    createTimeZoneConfiguration(false /* autoDetectionEnabled */);
             mTimeZoneDetectorService.updateConfiguration(autoDetectDisabledConfiguration);
 
             verify(mMockContext).enforceCallingPermission(
                     eq(android.Manifest.permission.WRITE_SECURE_SETTINGS),
                     anyString());
-            verify(mockListener, never()).onChange(any());
+            verify(mockListener, never()).onChange();
             verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
             reset(mockListenerBinder, mockListener, mMockContext);
         }
@@ -379,33 +353,22 @@
         mFakeTimeZoneDetectorStrategy.verifyDumpCalled();
     }
 
-    @Test
-    public void testHandleAutoTimeZoneConfigChanged() throws Exception {
-        mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged();
-        mTestHandler.assertTotalMessagesEnqueued(1);
-        mTestHandler.waitForMessagesToBeProcessed();
-        mFakeTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled();
-
-        mFakeTimeZoneDetectorStrategy.resetCallTracking();
-
-        mTimeZoneDetectorService.handleAutoTimeZoneConfigChanged();
-        mTestHandler.assertTotalMessagesEnqueued(2);
-        mTestHandler.waitForMessagesToBeProcessed();
-        mFakeTimeZoneDetectorStrategy.verifyHandleAutoTimeZoneConfigChangedCalled();
-    }
-
-    private static TimeZoneConfiguration createTimeZoneConfiguration(
-            boolean autoDetectionEnabled) {
-        return new TimeZoneConfiguration.Builder()
+    private static TimeZoneConfiguration createTimeZoneConfiguration(boolean autoDetectionEnabled) {
+        return new TimeZoneConfiguration.Builder(ARBITRARY_USER_ID)
                 .setAutoDetectionEnabled(autoDetectionEnabled)
                 .build();
     }
 
-    private static TimeZoneCapabilities createTimeZoneCapabilities() {
-        return new TimeZoneCapabilities.Builder(ARBITRARY_USER_ID)
-                .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
-                .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
-                .setSuggestManualTimeZone(CAPABILITY_POSSESSED)
+    private static ConfigurationInternal createConfigurationInternal(boolean autoDetectionEnabled) {
+        // Default geo detection settings from auto detection settings - they are not important to
+        // the tests.
+        final boolean geoDetectionEnabled = autoDetectionEnabled;
+        return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
+                .setAutoDetectionSupported(true)
+                .setUserConfigAllowed(true)
+                .setAutoDetectionEnabled(autoDetectionEnabled)
+                .setLocationEnabled(geoDetectionEnabled)
+                .setGeoDetectionEnabled(geoDetectionEnabled)
                 .build();
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index a6caa42..2bee5e5 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -23,10 +23,6 @@
 import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS;
 import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_MULTIPLE_ZONES_WITH_SAME_OFFSET;
 import static android.app.timezonedetector.TelephonyTimeZoneSuggestion.QUALITY_SINGLE_ZONE;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_ALLOWED;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_APPLICABLE;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_NOT_SUPPORTED;
-import static android.app.timezonedetector.TimeZoneCapabilities.CAPABILITY_POSSESSED;
 
 import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_HIGH;
 import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_HIGHEST;
@@ -37,9 +33,9 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -47,7 +43,6 @@
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality;
-import android.app.timezonedetector.TimeZoneCapabilities;
 import android.app.timezonedetector.TimeZoneConfiguration;
 import android.util.IndentingPrintWriter;
 
@@ -61,7 +56,6 @@
 import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -94,192 +88,149 @@
                     TELEPHONY_SCORE_HIGHEST),
     };
 
-    private static final TimeZoneConfiguration CONFIG_AUTO_ENABLED =
-            new TimeZoneConfiguration.Builder()
-                    .setAutoDetectionEnabled(true)
-                    .build();
-
-    private static final TimeZoneConfiguration CONFIG_AUTO_DISABLED =
-            new TimeZoneConfiguration.Builder()
+    private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_DISABLED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setUserConfigAllowed(false)
+                    .setAutoDetectionSupported(true)
                     .setAutoDetectionEnabled(false)
-                    .build();
-
-    private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_DISABLED =
-            new TimeZoneConfiguration.Builder()
+                    .setLocationEnabled(true)
                     .setGeoDetectionEnabled(false)
                     .build();
 
-    private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_ENABLED =
-            new TimeZoneConfiguration.Builder()
+    private static final ConfigurationInternal CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setUserConfigAllowed(false)
+                    .setAutoDetectionSupported(true)
+                    .setAutoDetectionEnabled(true)
+                    .setLocationEnabled(true)
                     .setGeoDetectionEnabled(true)
                     .build();
 
+    private static final ConfigurationInternal CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionSupported(false)
+                    .setAutoDetectionEnabled(false)
+                    .setLocationEnabled(true)
+                    .setGeoDetectionEnabled(false)
+                    .build();
+
+    private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_DISABLED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionSupported(true)
+                    .setAutoDetectionEnabled(false)
+                    .setLocationEnabled(true)
+                    .setGeoDetectionEnabled(false)
+                    .build();
+
+    private static final ConfigurationInternal CONFIG_INT_AUTO_DISABLED_GEO_ENABLED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionSupported(true)
+                    .setAutoDetectionEnabled(false)
+                    .setLocationEnabled(true)
+                    .setGeoDetectionEnabled(true)
+                    .build();
+
+    private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_DISABLED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setAutoDetectionSupported(true)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionEnabled(true)
+                    .setLocationEnabled(true)
+                    .setGeoDetectionEnabled(false)
+                    .build();
+
+    private static final ConfigurationInternal CONFIG_INT_AUTO_ENABLED_GEO_ENABLED =
+            new ConfigurationInternal.Builder(USER_ID)
+                    .setAutoDetectionSupported(true)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionEnabled(true)
+                    .setLocationEnabled(true)
+                    .setGeoDetectionEnabled(true)
+                    .build();
+
+    private static final TimeZoneConfiguration CONFIG_AUTO_DISABLED =
+            createConfig(false /* autoDetection */, null);
+    private static final TimeZoneConfiguration CONFIG_AUTO_ENABLED =
+            createConfig(true /* autoDetection */, null);
+    private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_ENABLED =
+            createConfig(null, true /* geoDetection */);
+    private static final TimeZoneConfiguration CONFIG_GEO_DETECTION_DISABLED =
+            createConfig(null, false /* geoDetection */);
+
     private TimeZoneDetectorStrategyImpl mTimeZoneDetectorStrategy;
     private FakeCallback mFakeCallback;
-    private MockStrategyListener mMockStrategyListener;
+    private MockConfigChangeListener mMockConfigChangeListener;
+
 
     @Before
     public void setUp() {
         mFakeCallback = new FakeCallback();
-        mMockStrategyListener = new MockStrategyListener();
+        mMockConfigChangeListener = new MockConfigChangeListener();
         mTimeZoneDetectorStrategy = new TimeZoneDetectorStrategyImpl(mFakeCallback);
-        mFakeCallback.setStrategyForSettingsCallbacks(mTimeZoneDetectorStrategy);
-        mTimeZoneDetectorStrategy.setStrategyListener(mMockStrategyListener);
+        mTimeZoneDetectorStrategy.addConfigChangeListener(mMockConfigChangeListener);
     }
 
     @Test
-    public void testGetCapabilities() {
-        new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        TimeZoneCapabilities expectedCapabilities = mFakeCallback.getCapabilities(USER_ID);
-        assertEquals(expectedCapabilities, mTimeZoneDetectorStrategy.getCapabilities(USER_ID));
-    }
-
-    @Test
-    public void testGetConfiguration() {
-        new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        TimeZoneConfiguration expectedConfiguration = mFakeCallback.getConfiguration(USER_ID);
-        assertTrue(expectedConfiguration.isComplete());
-        assertEquals(expectedConfiguration, mTimeZoneDetectorStrategy.getConfiguration(USER_ID));
-    }
-
-    @Test
-    public void testCapabilitiesTestInfra_unrestricted() {
-        Script script = new Script();
-
-        script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        {
-            // Check the fake test infra is doing what is expected.
-            TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
-            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled());
-            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled());
-            assertEquals(CAPABILITY_NOT_APPLICABLE, capabilities.getSuggestManualTimeZone());
-        }
-
-        script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        {
-            // Check the fake test infra is doing what is expected.
-            TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
-            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureAutoDetectionEnabled());
-            assertEquals(CAPABILITY_POSSESSED, capabilities.getConfigureGeoDetectionEnabled());
-            assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
-        }
-    }
-
-    @Test
-    public void testCapabilitiesTestInfra_restricted() {
-        Script script = new Script();
-
-        script.initializeUser(USER_ID, UserCase.RESTRICTED,
-                CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        {
-            // Check the fake test infra is doing what is expected.
-            TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
-            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled());
-            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled());
-            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone());
-        }
-
-        script.initializeUser(USER_ID, UserCase.RESTRICTED,
-                CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        {
-            // Check the fake test infra is doing what is expected.
-            TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
-            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureAutoDetectionEnabled());
-            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getConfigureGeoDetectionEnabled());
-            assertEquals(CAPABILITY_NOT_ALLOWED, capabilities.getSuggestManualTimeZone());
-        }
-    }
-
-    @Test
-    public void testCapabilitiesTestInfra_autoDetectNotSupported() {
-        Script script = new Script();
-
-        script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
-                CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        {
-            // Check the fake test infra is doing what is expected.
-            TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
-            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled());
-            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled());
-            assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
-        }
-
-        script.initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
-                CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED));
-        {
-            // Check the fake test infra is doing what is expected.
-            TimeZoneCapabilities capabilities = mFakeCallback.getCapabilities(USER_ID);
-            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureAutoDetectionEnabled());
-            assertEquals(CAPABILITY_NOT_SUPPORTED, capabilities.getConfigureGeoDetectionEnabled());
-            assertEquals(CAPABILITY_POSSESSED, capabilities.getSuggestManualTimeZone());
-        }
+    public void testGetCurrentUserConfiguration() {
+        new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
+        ConfigurationInternal expectedConfiguration =
+                mFakeCallback.getConfigurationInternal(USER_ID);
+        assertEquals(expectedConfiguration,
+                mTimeZoneDetectorStrategy.getCurrentUserConfigurationInternal());
     }
 
     @Test
     public void testUpdateConfiguration_unrestricted() {
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+        Script script = new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
 
         // Set the configuration with auto detection enabled.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */);
+        script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */);
 
         // Nothing should have happened: it was initialized in this state.
         script.verifyConfigurationNotChanged();
 
         // Update the configuration with auto detection disabled.
-        script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */);
+        script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */);
 
         // The settings should have been changed and the StrategyListener onChange() called.
-        script.verifyConfigurationChangedAndReset(USER_ID,
-                CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+        script.verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED);
 
         // Update the configuration with auto detection enabled.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */);
+        script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */);
 
         // The settings should have been changed and the StrategyListener onChange() called.
-        script.verifyConfigurationChangedAndReset(USER_ID,
-                CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+        script.verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
 
         // Update the configuration to enable geolocation time zone detection.
         script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_GEO_DETECTION_ENABLED,  true /* expectedResult */);
+                CONFIG_GEO_DETECTION_ENABLED,  true /* expectedResult */);
 
         // The settings should have been changed and the StrategyListener onChange() called.
-        script.verifyConfigurationChangedAndReset(USER_ID,
-                CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED));
+        script.verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED);
     }
 
     @Test
     public void testUpdateConfiguration_restricted() {
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.RESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+        Script script = new Script().initializeConfig(CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED);
 
         // Try to update the configuration with auto detection disabled.
-        script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_AUTO_DISABLED, false /* expectedResult */);
+        script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, false /* expectedResult */);
 
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
 
         // Update the configuration with auto detection enabled.
-        script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_AUTO_ENABLED,  false /* expectedResult */);
+        script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED,  false /* expectedResult */);
 
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
 
-        // Update the configuration to enable geolocation time zone detection.
+        // Try to  update the configuration to enable geolocation time zone detection.
         script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_GEO_DETECTION_ENABLED,  false /* expectedResult */);
+                CONFIG_GEO_DETECTION_ENABLED,  false /* expectedResult */);
 
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
@@ -287,20 +238,16 @@
 
     @Test
     public void testUpdateConfiguration_autoDetectNotSupported() {
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+        Script script = new Script().initializeConfig(CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED);
 
         // Try to update the configuration with auto detection disabled.
-        script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_AUTO_DISABLED, false /* expectedResult */);
+        script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, false /* expectedResult */);
 
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
 
         // Update the configuration with auto detection enabled.
-        script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_AUTO_ENABLED, false /* expectedResult */);
+        script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, false /* expectedResult */);
 
         // The settings should not have been changed: user shouldn't have the capabilities.
         script.verifyConfigurationNotChanged();
@@ -313,8 +260,7 @@
         TelephonyTimeZoneSuggestion slotIndex2TimeZoneSuggestion =
                 createEmptySlotIndex2Suggestion();
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         script.simulateTelephonyTimeZoneSuggestion(slotIndex1TimeZoneSuggestion)
@@ -359,9 +305,7 @@
         TelephonyTestCase testCase2 = newTelephonyTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
                 QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH);
 
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+        Script script = new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
 
         // A low quality suggestions will not be taken: The device time zone setting is left
         // uninitialized.
@@ -426,8 +370,7 @@
 
         for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) {
             // Start with the device in a known state.
-            script.initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                    CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+            script.initializeConfig(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED)
                     .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
             TelephonyTimeZoneSuggestion suggestion =
@@ -447,8 +390,7 @@
                     mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
 
             // Toggling the time zone setting on should cause the device setting to be set.
-            script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED,
-                    true /* expectedResult */);
+            script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */);
 
             // When time zone detection is already enabled the suggestion (if it scores highly
             // enough) should be set immediately.
@@ -465,8 +407,7 @@
                     mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
 
             // Toggling the time zone setting should off should do nothing.
-            script.simulateUpdateConfiguration(
-                    USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
+            script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */)
                     .verifyTimeZoneNotChanged();
 
             // Assert internal service state.
@@ -480,8 +421,7 @@
     @Test
     public void testTelephonySuggestionsSingleSlotId() {
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         for (TelephonyTestCase testCase : TELEPHONY_TEST_CASES) {
@@ -546,8 +486,7 @@
                         TELEPHONY_SCORE_NONE);
 
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID)
                 // Initialize the latest suggestions as empty so we don't need to worry about nulls
                 // below for the first loop.
@@ -632,9 +571,7 @@
      */
     @Test
     public void testTelephonySuggestionStrategyDoesNotAssumeCurrentSetting_autoTelephony() {
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED));
+        Script script = new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
 
         TelephonyTestCase testCase = newTelephonyTestCase(
                 MATCH_TYPE_NETWORK_COUNTRY_AND_OFFSET, QUALITY_SINGLE_ZONE, TELEPHONY_SCORE_HIGH);
@@ -652,40 +589,39 @@
 
         // Toggling time zone detection should set the device time zone only if the current setting
         // value is different from the most recent telephony suggestion.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
+        script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */)
                 .verifyTimeZoneNotChanged()
-                .simulateUpdateConfiguration(
-                        USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
+                .simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */)
                 .verifyTimeZoneNotChanged();
 
         // Simulate a user turning auto detection off, a new suggestion being made while auto
         // detection is off, and the user turning it on again.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
+        script.simulateUpdateConfiguration(CONFIG_AUTO_DISABLED, true /* expectedResult */)
                 .simulateTelephonyTimeZoneSuggestion(newYorkSuggestion)
                 .verifyTimeZoneNotChanged();
         // Latest suggestion should be used.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
+        script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */)
                 .verifyTimeZoneChangedAndReset(newYorkSuggestion);
     }
 
     @Test
-    public void testManualSuggestion_autoDetectionEnabled_autoTelephony() {
-        checkManualSuggestion_autoDetectionEnabled(false /* geoDetectionEnabled */);
+    public void testManualSuggestion_unrestricted_autoDetectionEnabled_autoTelephony() {
+        checkManualSuggestion_unrestricted_autoDetectionEnabled(false /* geoDetectionEnabled */);
     }
 
     @Test
-    public void testManualSuggestion_autoDetectionEnabled_autoGeo() {
-        checkManualSuggestion_autoDetectionEnabled(true /* geoDetectionEnabled */);
+    public void testManualSuggestion_unrestricted_autoDetectionEnabled_autoGeo() {
+        checkManualSuggestion_unrestricted_autoDetectionEnabled(true /* geoDetectionEnabled */);
     }
 
-    private void checkManualSuggestion_autoDetectionEnabled(boolean geoDetectionEnabled) {
-        TimeZoneConfiguration geoTzEnabledConfig =
-                new TimeZoneConfiguration.Builder()
+    private void checkManualSuggestion_unrestricted_autoDetectionEnabled(
+            boolean geoDetectionEnabled) {
+        ConfigurationInternal geoTzEnabledConfig =
+                new ConfigurationInternal.Builder(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED)
                         .setGeoDetectionEnabled(geoDetectionEnabled)
                         .build();
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(geoTzEnabledConfig))
+                .initializeConfig(geoTzEnabledConfig)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         // Auto time zone detection is enabled so the manual suggestion should be ignored.
@@ -697,35 +633,19 @@
     @Test
     public void testManualSuggestion_restricted_simulateAutoTimeZoneEnabled() {
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.RESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_USER_RESTRICTED_AUTO_ENABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
-        // Auto time zone detection is enabled so the manual suggestion should be ignored.
+        // User is restricted so the manual suggestion should be ignored.
         script.simulateManualTimeZoneSuggestion(
                 USER_ID, createManualSuggestion("Europe/Paris"), false /* expectedResult */)
-            .verifyTimeZoneNotChanged();
-    }
-
-    @Test
-    public void testManualSuggestion_autoDetectNotSupported_simulateAutoTimeZoneEnabled() {
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_DISABLED))
-                .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
-
-        // Auto time zone detection is enabled so the manual suggestion should be ignored.
-        ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
-        script.simulateManualTimeZoneSuggestion(
-                USER_ID, manualSuggestion, true /* expectedResult */)
-            .verifyTimeZoneChangedAndReset(manualSuggestion);
+                .verifyTimeZoneNotChanged();
     }
 
     @Test
     public void testManualSuggestion_unrestricted_autoTimeZoneDetectionDisabled() {
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         // Auto time zone detection is disabled so the manual suggestion should be used.
@@ -738,8 +658,7 @@
     @Test
     public void testManualSuggestion_restricted_autoTimeZoneDetectionDisabled() {
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.RESTRICTED,
-                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_USER_RESTRICTED_AUTO_DISABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         // Restricted users do not have the capability.
@@ -750,10 +669,9 @@
     }
 
     @Test
-    public void testManualSuggestion_autoDetectNotSupported_autoTimeZoneDetectionDisabled() {
+    public void testManualSuggestion_autoDetectNotSupported() {
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.AUTO_DETECT_NOT_SUPPORTED,
-                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_AUTO_DETECT_NOT_SUPPORTED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         // Unrestricted users have the capability.
@@ -765,9 +683,7 @@
 
     @Test
     public void testGeoSuggestion_uncertain() {
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
+        Script script = new Script().initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         GeolocationTimeZoneSuggestion uncertainSuggestion = createUncertainGeoLocationSuggestion();
@@ -783,8 +699,7 @@
     @Test
     public void testGeoSuggestion_noZones() {
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
+                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         GeolocationTimeZoneSuggestion noZonesSuggestion = createGeoLocationSuggestion(list());
@@ -802,8 +717,7 @@
                 createGeoLocationSuggestion(list("Europe/London"));
 
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
+                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         script.simulateGeolocationTimeZoneSuggestion(suggestion)
@@ -828,8 +742,7 @@
                 createGeoLocationSuggestion(list("Europe/Paris"));
 
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED))
+                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         script.simulateGeolocationTimeZoneSuggestion(londonOnlySuggestion)
@@ -856,72 +769,27 @@
                 mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
     }
 
-    /**
-     * Confirms that toggling the auto time zone detection enabled setting has the expected behavior
-     * when the strategy is "opinionated" and "un-opinionated" when in geolocation detection is
-     * enabled.
-     */
     @Test
-    public void testTogglingAutoDetectionEnabled_autoGeo() {
-        GeolocationTimeZoneSuggestion geolocationSuggestion =
+    public void testGeoSuggestion_togglingGeoDetectionClearsLastSuggestion() {
+        GeolocationTimeZoneSuggestion suggestion =
                 createGeoLocationSuggestion(list("Europe/London"));
-        GeolocationTimeZoneSuggestion uncertainGeolocationSuggestion =
-                createUncertainGeoLocationSuggestion();
-        ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
 
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_ENABLED))
+                .initializeConfig(CONFIG_INT_AUTO_ENABLED_GEO_ENABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
-        script.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion);
-
-        // When time zone detection is not enabled, the time zone suggestion will not be set.
-        script.verifyTimeZoneNotChanged();
-
-        // Assert internal service state.
-        assertEquals(geolocationSuggestion,
-                mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
-
-        // Toggling the time zone setting on should cause the device setting to be set.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
+        script.simulateGeolocationTimeZoneSuggestion(suggestion)
                 .verifyTimeZoneChangedAndReset("Europe/London");
 
-        // Toggling the time zone setting should off should do nothing because the device is now
-        // set to that time zone.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
-                .verifyTimeZoneNotChanged()
-                .simulateUpdateConfiguration(
-                        USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
-                .verifyTimeZoneNotChanged();
+        // Assert internal service state.
+        assertEquals(suggestion, mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
 
-        // Now toggle auto time zone setting, and confirm it is opinionated.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
-                .simulateManualTimeZoneSuggestion(
-                        USER_ID, manualSuggestion, true /* expectedResult */)
-                .verifyTimeZoneChangedAndReset(manualSuggestion)
-                .simulateUpdateConfiguration(
-                        USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
-                .verifyTimeZoneChangedAndReset("Europe/London");
-
-        // Now withdraw the geolocation suggestion, and assert the strategy is no longer
-        // opinionated.
-        /* expectedResult */
-        script.simulateGeolocationTimeZoneSuggestion(uncertainGeolocationSuggestion)
-                .verifyTimeZoneNotChanged()
-                .simulateUpdateConfiguration(
-                        USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
-                .verifyTimeZoneNotChanged()
-                .simulateManualTimeZoneSuggestion(
-                        USER_ID, manualSuggestion, true /* expectedResult */)
-                .verifyTimeZoneChangedAndReset(manualSuggestion)
-                .simulateUpdateConfiguration(
-                        USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
-                .verifyTimeZoneNotChanged();
+        // Turn off geo detection and verify the latest suggestion is cleared.
+        script.simulateUpdateConfiguration(CONFIG_GEO_DETECTION_DISABLED, true)
+                .verifyConfigurationChangedAndReset(CONFIG_INT_AUTO_ENABLED_GEO_DISABLED);
 
         // Assert internal service state.
-        assertEquals(uncertainGeolocationSuggestion,
-                mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+        assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
     }
 
     /**
@@ -937,88 +805,48 @@
                 "Europe/Paris");
 
         Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         // Add suggestions. Nothing should happen as time zone detection is disabled.
         script.simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
                 .verifyTimeZoneNotChanged();
+
+        // Geolocation suggestions are only stored when geolocation detection is enabled.
+        assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+
         script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion)
                 .verifyTimeZoneNotChanged();
 
-        // Assert internal service state.
-        assertEquals(geolocationSuggestion,
-                mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
+        // Telephony suggestions are always stored.
         assertEquals(telephonySuggestion,
                 mTimeZoneDetectorStrategy.getLatestTelephonySuggestion(SLOT_INDEX1).suggestion);
 
         // Toggling the time zone detection enabled setting on should cause the device setting to be
         // set from the telephony signal, as we've started with geolocation time zone detection
         // disabled.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
+        script.simulateUpdateConfiguration(CONFIG_AUTO_ENABLED, true /* expectedResult */)
                 .verifyTimeZoneChangedAndReset(telephonySuggestion);
 
-        // Changing the detection to enable geo detection should cause the device tz setting to
-        // change to the geo suggestion.
-        script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */)
+        // Changing the detection to enable geo detection won't cause the device tz setting to
+        // change because the geo suggestion is empty.
+        script.simulateUpdateConfiguration(CONFIG_GEO_DETECTION_ENABLED, true /* expectedResult */)
+                .verifyTimeZoneNotChanged()
+                .simulateGeolocationTimeZoneSuggestion(geolocationSuggestion)
                 .verifyTimeZoneChangedAndReset(geolocationSuggestion.getZoneIds().get(0));
 
         // Changing the detection to disable geo detection should cause the device tz setting to
         // change to the telephony suggestion.
-        script.simulateUpdateConfiguration(
-                USER_ID, CONFIG_GEO_DETECTION_DISABLED, true /* expectedResult */)
+        script.simulateUpdateConfiguration(CONFIG_GEO_DETECTION_DISABLED, true /* expectedResult */)
                 .verifyTimeZoneChangedAndReset(telephonySuggestion);
-    }
 
-    /**
-     * The {@link TimeZoneDetectorStrategyImpl.Callback} is left to detect whether changing the time
-     * zone is actually necessary. This test proves that the strategy doesn't assume it knows the
-     * current setting.
-     */
-    @Test
-    public void testTimeZoneDetectorStrategyDoesNotAssumeCurrentSetting_autoGeo() {
-        GeolocationTimeZoneSuggestion losAngelesSuggestion =
-                createGeoLocationSuggestion(list("America/Los_Angeles"));
-        GeolocationTimeZoneSuggestion newYorkSuggestion =
-                createGeoLocationSuggestion(list("America/New_York"));
-
-        Script script = new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_ENABLED.with(CONFIG_GEO_DETECTION_ENABLED));
-
-        // Initialization.
-        script.simulateGeolocationTimeZoneSuggestion(losAngelesSuggestion)
-                .verifyTimeZoneChangedAndReset("America/Los_Angeles");
-        // Suggest it again - it should not be set because it is already set.
-        script.simulateGeolocationTimeZoneSuggestion(losAngelesSuggestion)
-                .verifyTimeZoneNotChanged();
-
-        // Toggling time zone detection should set the device time zone only if the current setting
-        // value is different from the most recent telephony suggestion.
-        /* expectedResult */
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
-                .verifyTimeZoneNotChanged()
-                .simulateUpdateConfiguration(
-                        USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
-                .verifyTimeZoneNotChanged();
-
-        // Simulate a user turning auto detection off, a new suggestion being made while auto
-        // detection is off, and the user turning it on again.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_DISABLED, true /* expectedResult */)
-                .simulateGeolocationTimeZoneSuggestion(newYorkSuggestion)
-                .verifyTimeZoneNotChanged();
-        // Latest suggestion should be used.
-        script.simulateUpdateConfiguration(USER_ID, CONFIG_AUTO_ENABLED, true /* expectedResult */)
-                .verifyTimeZoneChangedAndReset("America/New_York");
+        assertNull(mTimeZoneDetectorStrategy.getLatestGeolocationSuggestion());
     }
 
     @Test
     public void testAddDumpable() {
         new Script()
-                .initializeUser(USER_ID, UserCase.UNRESTRICTED,
-                        CONFIG_AUTO_DISABLED.with(CONFIG_GEO_DETECTION_DISABLED))
+                .initializeConfig(CONFIG_INT_AUTO_DISABLED_GEO_DISABLED)
                 .initializeTimeZoneSetting(ARBITRARY_TIME_ZONE_ID);
 
         AtomicBoolean dumpCalled = new AtomicBoolean(false);
@@ -1069,25 +897,26 @@
         return suggestion;
     }
 
+    private static TimeZoneConfiguration createConfig(
+            @Nullable Boolean autoDetection, @Nullable Boolean geoDetection) {
+        TimeZoneConfiguration.Builder builder = new TimeZoneConfiguration.Builder(USER_ID);
+        if (autoDetection != null) {
+            builder.setAutoDetectionEnabled(autoDetection);
+        }
+        if (geoDetection != null) {
+            builder.setGeoDetectionEnabled(geoDetection);
+        }
+        return builder.build();
+    }
+
     static class FakeCallback implements TimeZoneDetectorStrategyImpl.Callback {
 
-        private TimeZoneCapabilities mCapabilities;
-        private final TestState<UserConfiguration> mConfiguration = new TestState<>();
+        private final TestState<ConfigurationInternal> mConfigurationInternal = new TestState<>();
         private final TestState<String> mTimeZoneId = new TestState<>();
-        private TimeZoneDetectorStrategyImpl mStrategy;
+        private ConfigurationChangeListener mConfigChangeListener;
 
-        void setStrategyForSettingsCallbacks(TimeZoneDetectorStrategyImpl strategy) {
-            assertNotNull(strategy);
-            mStrategy = strategy;
-        }
-
-        void initializeUser(@UserIdInt int userId, TimeZoneCapabilities capabilities,
-                TimeZoneConfiguration configuration) {
-            assertEquals(userId, capabilities.getUserId());
-            mCapabilities = capabilities;
-            assertTrue("Configuration must be complete when initializing, config=" + configuration,
-                    configuration.isComplete());
-            mConfiguration.init(new UserConfiguration(userId, configuration));
+        void initializeConfig(ConfigurationInternal configurationInternal) {
+            mConfigurationInternal.init(configurationInternal);
         }
 
         void initializeTimeZoneSetting(String zoneId) {
@@ -1095,43 +924,22 @@
         }
 
         @Override
-        public TimeZoneCapabilities getCapabilities(@UserIdInt int userId) {
-            assertEquals(userId, mCapabilities.getUserId());
-            return mCapabilities;
+        public void setConfigChangeListener(ConfigurationChangeListener listener) {
+            mConfigChangeListener = listener;
         }
 
         @Override
-        public TimeZoneConfiguration getConfiguration(@UserIdInt int userId) {
-            UserConfiguration latest = mConfiguration.getLatest();
-            assertEquals(userId, latest.userId);
-            return latest.configuration;
-        }
-
-        @Override
-        public void setConfiguration(@UserIdInt int userId, TimeZoneConfiguration newConfig) {
-            assertNotNull(newConfig);
-            assertTrue(newConfig.isComplete());
-
-            UserConfiguration latestUserConfig = mConfiguration.getLatest();
-            assertEquals(userId, latestUserConfig.userId);
-            TimeZoneConfiguration oldConfig = latestUserConfig.configuration;
-
-            mConfiguration.set(new UserConfiguration(userId, newConfig));
-
-            if (!newConfig.equals(oldConfig)) {
-                // Simulate what happens when the auto detection configuration is changed.
-                mStrategy.handleAutoTimeZoneConfigChanged();
+        public ConfigurationInternal getConfigurationInternal(int userId) {
+            ConfigurationInternal configuration = mConfigurationInternal.getLatest();
+            if (userId != configuration.getUserId()) {
+                fail("FakeCallback does not support multiple users.");
             }
+            return configuration;
         }
 
         @Override
-        public boolean isAutoDetectionEnabled() {
-            return mConfiguration.getLatest().configuration.isAutoDetectionEnabled();
-        }
-
-        @Override
-        public boolean isGeoDetectionEnabled() {
-            return mConfiguration.getLatest().configuration.isGeoDetectionEnabled();
+        public int getCurrentUserId() {
+            return mConfigurationInternal.getLatest().getUserId();
         }
 
         @Override
@@ -1149,9 +957,25 @@
             mTimeZoneId.set(zoneId);
         }
 
+        @Override
+        public void storeConfiguration(TimeZoneConfiguration newConfiguration) {
+            ConfigurationInternal oldConfiguration = mConfigurationInternal.getLatest();
+            if (newConfiguration.getUserId() != oldConfiguration.getUserId()) {
+                fail("FakeCallback does not support multiple users");
+            }
+
+            ConfigurationInternal mergedConfiguration = oldConfiguration.merge(newConfiguration);
+            if (!mergedConfiguration.equals(oldConfiguration)) {
+                mConfigurationInternal.set(mergedConfiguration);
+
+                // Note: Unlike the real callback impl, the listener is invoked synchronously.
+                mConfigChangeListener.onChange();
+            }
+        }
+
         void assertKnownUser(int userId) {
-            assertEquals(userId, mCapabilities.getUserId());
-            assertEquals(userId, mConfiguration.getLatest().userId);
+            assertEquals("FakeCallback does not support multiple users",
+                    mConfigurationInternal.getLatest().getUserId(), userId);
         }
 
         void assertTimeZoneNotChanged() {
@@ -1166,43 +990,7 @@
 
         void commitAllChanges() {
             mTimeZoneId.commitLatest();
-            mConfiguration.commitLatest();
-        }
-    }
-
-    private static final class UserConfiguration {
-        public final @UserIdInt int userId;
-        public final TimeZoneConfiguration configuration;
-
-        UserConfiguration(int userId, TimeZoneConfiguration configuration) {
-            this.userId = userId;
-            this.configuration = configuration;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            UserConfiguration that = (UserConfiguration) o;
-            return userId == that.userId
-                    && Objects.equals(configuration, that.configuration);
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(userId, configuration);
-        }
-
-        @Override
-        public String toString() {
-            return "UserConfiguration{"
-                    + "userId=" + userId
-                    + ", configuration=" + configuration
-                    + '}';
+            mConfigurationInternal.commitLatest();
         }
     }
 
@@ -1255,64 +1043,14 @@
         }
     }
 
-    /** Simulated user test cases. */
-    enum UserCase {
-        /** A catch-all for users that can set auto time zone config. */
-        UNRESTRICTED,
-        /** A catch-all for users that can't set auto time zone config. */
-        RESTRICTED,
-        /**
-         * Like {@link #UNRESTRICTED}, but auto tz detection is not
-         * supported on the device.
-         */
-        AUTO_DETECT_NOT_SUPPORTED,
-    }
-
-    /**
-     * Creates a {@link TimeZoneCapabilities} object for a user in the specific role with the
-     * supplied configuration.
-     */
-    private static TimeZoneCapabilities createCapabilities(
-            int userId, UserCase userCase, TimeZoneConfiguration configuration) {
-        switch (userCase) {
-            case UNRESTRICTED: {
-                int suggestManualTimeZoneCapability = configuration.isAutoDetectionEnabled()
-                        ? CAPABILITY_NOT_APPLICABLE : CAPABILITY_POSSESSED;
-                return new TimeZoneCapabilities.Builder(userId)
-                        .setConfigureAutoDetectionEnabled(CAPABILITY_POSSESSED)
-                        .setConfigureGeoDetectionEnabled(CAPABILITY_POSSESSED)
-                        .setSuggestManualTimeZone(suggestManualTimeZoneCapability)
-                        .build();
-            }
-            case RESTRICTED: {
-                return new TimeZoneCapabilities.Builder(userId)
-                        .setConfigureAutoDetectionEnabled(CAPABILITY_NOT_ALLOWED)
-                        .setConfigureGeoDetectionEnabled(CAPABILITY_NOT_ALLOWED)
-                        .setSuggestManualTimeZone(CAPABILITY_NOT_ALLOWED)
-                        .build();
-            }
-            case AUTO_DETECT_NOT_SUPPORTED: {
-                return new TimeZoneCapabilities.Builder(userId)
-                        .setConfigureAutoDetectionEnabled(CAPABILITY_NOT_SUPPORTED)
-                        .setConfigureGeoDetectionEnabled(CAPABILITY_NOT_SUPPORTED)
-                        .setSuggestManualTimeZone(CAPABILITY_POSSESSED)
-                        .build();
-            }
-            default:
-                throw new AssertionError(userCase + " not recognized");
-        }
-    }
-
     /**
      * A "fluent" class allows reuse of code in tests: initialization, simulation and verification
      * logic.
      */
     private class Script {
 
-        Script initializeUser(
-                @UserIdInt int userId, UserCase userCase, TimeZoneConfiguration configuration) {
-            TimeZoneCapabilities capabilities = createCapabilities(userId, userCase, configuration);
-            mFakeCallback.initializeUser(userId, capabilities, configuration);
+        Script initializeConfig(ConfigurationInternal configuration) {
+            mFakeCallback.initializeConfig(configuration);
             return this;
         }
 
@@ -1326,10 +1064,9 @@
          * the return value.
          */
         Script simulateUpdateConfiguration(
-                @UserIdInt int userId, TimeZoneConfiguration configuration,
-                boolean expectedResult) {
+                TimeZoneConfiguration configuration, boolean expectedResult) {
             assertEquals(expectedResult,
-                    mTimeZoneDetectorStrategy.updateConfiguration(userId, configuration));
+                    mTimeZoneDetectorStrategy.updateConfiguration(configuration));
             return this;
         }
 
@@ -1392,16 +1129,14 @@
         /**
          * Verifies that the configuration has been changed to the expected value.
          */
-        Script verifyConfigurationChangedAndReset(
-                @UserIdInt int userId, TimeZoneConfiguration expected) {
-            mFakeCallback.mConfiguration.assertHasBeenSet();
-            UserConfiguration expectedUserConfig = new UserConfiguration(userId, expected);
-            assertEquals(expectedUserConfig, mFakeCallback.mConfiguration.getLatest());
+        Script verifyConfigurationChangedAndReset(ConfigurationInternal expected) {
+            mFakeCallback.mConfigurationInternal.assertHasBeenSet();
+            assertEquals(expected, mFakeCallback.mConfigurationInternal.getLatest());
             mFakeCallback.commitAllChanges();
 
             // Also confirm the listener triggered.
-            mMockStrategyListener.verifyOnConfigurationChangedCalled();
-            mMockStrategyListener.reset();
+            mMockConfigChangeListener.verifyOnChangeCalled();
+            mMockConfigChangeListener.reset();
             return this;
         }
 
@@ -1410,10 +1145,10 @@
          * {@link TimeZoneConfiguration} have been changed.
          */
         Script verifyConfigurationNotChanged() {
-            mFakeCallback.mConfiguration.assertHasNotBeenSet();
+            mFakeCallback.mConfigurationInternal.assertHasNotBeenSet();
 
             // Also confirm the listener did not trigger.
-            mMockStrategyListener.verifyOnConfigurationChangedNotCalled();
+            mMockConfigChangeListener.verifyOnChangeNotCalled();
             return this;
         }
 
@@ -1448,24 +1183,24 @@
         return new TelephonyTestCase(matchType, quality, expectedScore);
     }
 
-    private static class MockStrategyListener implements TimeZoneDetectorStrategy.StrategyListener {
-        private boolean mOnConfigurationChangedCalled;
+    private static class MockConfigChangeListener implements ConfigurationChangeListener {
+        private boolean mOnChangeCalled;
 
         @Override
-        public void onConfigurationChanged() {
-            mOnConfigurationChangedCalled = true;
+        public void onChange() {
+            mOnChangeCalled = true;
         }
 
-        void verifyOnConfigurationChangedCalled() {
-            assertTrue(mOnConfigurationChangedCalled);
+        void verifyOnChangeCalled() {
+            assertTrue(mOnChangeCalled);
         }
 
-        void verifyOnConfigurationChangedNotCalled() {
-            assertFalse(mOnConfigurationChangedCalled);
+        void verifyOnChangeNotCalled() {
+            assertFalse(mOnChangeCalled);
         }
 
         void reset() {
-            mOnConfigurationChangedCalled = false;
+            mOnChangeCalled = false;
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java b/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java
index d5aee5d..2c719ff 100644
--- a/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/uri/UriGrantsMockContext.java
@@ -120,17 +120,17 @@
         LocalServices.addService(PackageManagerInternal.class, mPmInternal);
 
         for (int userId : new int[] { USER_PRIMARY, USER_SECONDARY }) {
-            when(mPmInternal.getPackageUidInternal(eq(PKG_SOCIAL), anyInt(), eq(userId)))
+            when(mPmInternal.getPackageUid(eq(PKG_SOCIAL), anyInt(), eq(userId)))
                     .thenReturn(UserHandle.getUid(userId, UID_SOCIAL));
-            when(mPmInternal.getPackageUidInternal(eq(PKG_CAMERA), anyInt(), eq(userId)))
+            when(mPmInternal.getPackageUid(eq(PKG_CAMERA), anyInt(), eq(userId)))
                     .thenReturn(UserHandle.getUid(userId, UID_CAMERA));
-            when(mPmInternal.getPackageUidInternal(eq(PKG_PRIVATE), anyInt(), eq(userId)))
+            when(mPmInternal.getPackageUid(eq(PKG_PRIVATE), anyInt(), eq(userId)))
                     .thenReturn(UserHandle.getUid(userId, UID_PRIVATE));
-            when(mPmInternal.getPackageUidInternal(eq(PKG_PUBLIC), anyInt(), eq(userId)))
+            when(mPmInternal.getPackageUid(eq(PKG_PUBLIC), anyInt(), eq(userId)))
                     .thenReturn(UserHandle.getUid(userId, UID_PUBLIC));
-            when(mPmInternal.getPackageUidInternal(eq(PKG_FORCE), anyInt(), eq(userId)))
+            when(mPmInternal.getPackageUid(eq(PKG_FORCE), anyInt(), eq(userId)))
                     .thenReturn(UserHandle.getUid(userId, UID_FORCE));
-            when(mPmInternal.getPackageUidInternal(eq(PKG_COMPLEX), anyInt(), eq(userId)))
+            when(mPmInternal.getPackageUid(eq(PKG_COMPLEX), anyInt(), eq(userId)))
                     .thenReturn(UserHandle.getUid(userId, UID_COMPLEX));
 
             when(mPmInternal.resolveContentProvider(eq(PKG_CAMERA), anyInt(), eq(userId)))
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 f860e17..59f0a79 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -544,7 +544,7 @@
         mActivity = new ActivityBuilder(mAtm)
                 .setTask(mTask)
                 .setLaunchTaskBehind(true)
-                .setConfigChanges(CONFIG_ORIENTATION)
+                .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
                 .build();
         mActivity.setState(Task.ActivityState.STOPPED, "Testing");
 
@@ -1190,7 +1190,7 @@
 
         assertEquals(DESTROYING, mActivity.getState());
         assertTrue(mActivity.finishing);
-        verify(mActivity).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+        verify(mActivity).destroyImmediately(anyString());
     }
 
     /**
@@ -1214,7 +1214,7 @@
 
         // Verify that the activity was not actually destroyed, but waits for next one to come up
         // instead.
-        verify(mActivity, never()).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+        verify(mActivity, never()).destroyImmediately(anyString());
         assertEquals(FINISHING, mActivity.getState());
         assertTrue(mActivity.mStackSupervisor.mFinishingActivities.contains(mActivity));
     }
@@ -1238,7 +1238,7 @@
         mActivity.completeFinishing("test");
 
         // Verify that the activity is not destroyed immediately, but waits for next one to come up.
-        verify(mActivity, never()).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+        verify(mActivity, never()).destroyImmediately(anyString());
         assertEquals(FINISHING, mActivity.getState());
         assertTrue(mActivity.mStackSupervisor.mFinishingActivities.contains(mActivity));
     }
@@ -1250,26 +1250,26 @@
     @Test
     public void testDestroyImmediately_hadApp_finishing() {
         mActivity.finishing = true;
-        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+        mActivity.destroyImmediately("test");
 
         assertEquals(DESTROYING, mActivity.getState());
     }
 
     /**
      * Test that the activity will be moved to destroyed state immediately if it was not marked as
-     * finishing before {@link ActivityRecord#destroyImmediately(boolean, String)}.
+     * finishing before {@link ActivityRecord#destroyImmediately(String)}.
      */
     @Test
     public void testDestroyImmediately_hadApp_notFinishing() {
         mActivity.finishing = false;
-        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+        mActivity.destroyImmediately("test");
 
         assertEquals(DESTROYED, mActivity.getState());
     }
 
     /**
      * Test that an activity with no process attached and that is marked as finishing will be
-     * removed from task when {@link ActivityRecord#destroyImmediately(boolean, String)} is called.
+     * removed from task when {@link ActivityRecord#destroyImmediately(String)} is called.
      */
     @Test
     public void testDestroyImmediately_noApp_finishing() {
@@ -1277,7 +1277,7 @@
         mActivity.finishing = true;
         final Task task = mActivity.getTask();
 
-        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+        mActivity.destroyImmediately("test");
 
         assertEquals(DESTROYED, mActivity.getState());
         assertNull(mActivity.getTask());
@@ -1294,7 +1294,7 @@
         mActivity.finishing = false;
         final Task task = mActivity.getTask();
 
-        mActivity.destroyImmediately(false /* removeFromApp */, "test");
+        mActivity.destroyImmediately("test");
 
         assertEquals(DESTROYED, mActivity.getState());
         assertEquals(task, mActivity.getTask());
@@ -1310,7 +1310,7 @@
 
         mActivity.safelyDestroy("test");
 
-        verify(mActivity, never()).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+        verify(mActivity, never()).destroyImmediately(anyString());
     }
 
     /**
@@ -1322,7 +1322,7 @@
 
         mActivity.safelyDestroy("test");
 
-        verify(mActivity).destroyImmediately(eq(true) /* removeFromApp */, anyString());
+        verify(mActivity).destroyImmediately(anyString());
     }
 
     @Test
@@ -1655,13 +1655,13 @@
         assertEquals(0, thirdActivity.getMergedOverrideConfiguration()
                 .diff(wpc.getRequestedOverrideConfiguration()));
 
-        secondActivity.destroyImmediately(true, "");
+        secondActivity.destroyImmediately("");
 
         assertTrue(wpc.registeredForActivityConfigChanges());
         assertEquals(0, thirdActivity.getMergedOverrideConfiguration()
                 .diff(wpc.getRequestedOverrideConfiguration()));
 
-        firstActivity.destroyImmediately(true, "");
+        firstActivity.destroyImmediately("");
 
         assertTrue(wpc.registeredForActivityConfigChanges());
         assertEquals(0, thirdActivity.getMergedOverrideConfiguration()
@@ -1714,6 +1714,27 @@
 
     }
 
+    @Test
+    public void testProcessInfoUpdateWhenSetState() {
+        spyOn(mActivity.app);
+        verifyProcessInfoUpdate(RESUMED, true /* shouldUpdate */, true /* activityChange */);
+        verifyProcessInfoUpdate(PAUSED, false /* shouldUpdate */, false /* activityChange */);
+        verifyProcessInfoUpdate(STOPPED, false /* shouldUpdate */, false /* activityChange */);
+        verifyProcessInfoUpdate(STARTED, true /* shouldUpdate */, true /* activityChange */);
+
+        mActivity.app.removeActivity(mActivity, true /* keepAssociation */);
+        verifyProcessInfoUpdate(DESTROYING, true /* shouldUpdate */, false /* activityChange */);
+        verifyProcessInfoUpdate(DESTROYED, true /* shouldUpdate */, false /* activityChange */);
+    }
+
+    private void verifyProcessInfoUpdate(ActivityState state, boolean shouldUpdate,
+            boolean activityChange) {
+        reset(mActivity.app);
+        mActivity.setState(state, "test");
+        verify(mActivity.app, times(shouldUpdate ? 1 : 0)).updateProcessInfo(anyBoolean(),
+                eq(activityChange), anyBoolean(), anyBoolean());
+    }
+
     /**
      * Creates an activity on display. For non-default display request it will also create a new
      * display with custom DisplayInfo.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index e2948a7..4cad397 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -1202,19 +1202,22 @@
     @Test
     public void testShouldSleepActivities() {
         // When focused activity and keyguard is going away, we should not sleep regardless
-        // of the display state
+        // of the display state, but keyguard-going-away should only take effects on default
+        // display since there is no keyguard on secondary displays (yet).
         verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/,
-                true /* displaySleeping */, false /* expected*/);
+                true /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */);
+        verifyShouldSleepActivities(true /* focusedStack */, true /*keyguardGoingAway*/,
+                true /* displaySleeping */, false /* isDefaultDisplay */, true /* expected */);
 
         // When not the focused stack, defer to display sleeping state.
         verifyShouldSleepActivities(false /* focusedStack */, true /*keyguardGoingAway*/,
-                true /* displaySleeping */, true /* expected*/);
+                true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */);
 
         // If keyguard is going away, defer to the display sleeping state.
         verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
-                true /* displaySleeping */, true /* expected*/);
+                true /* displaySleeping */, true /* isDefaultDisplay */, true /* expected */);
         verifyShouldSleepActivities(true /* focusedStack */, false /*keyguardGoingAway*/,
-                false /* displaySleeping */, false /* expected*/);
+                false /* displaySleeping */, true /* isDefaultDisplay */, false /* expected */);
     }
 
     @Test
@@ -1423,9 +1426,11 @@
     }
 
     private void verifyShouldSleepActivities(boolean focusedStack,
-            boolean keyguardGoingAway, boolean displaySleeping, boolean expected) {
+            boolean keyguardGoingAway, boolean displaySleeping, boolean isDefaultDisplay,
+            boolean expected) {
         final DisplayContent display = mock(DisplayContent.class);
         final KeyguardController keyguardController = mSupervisor.getKeyguardController();
+        display.isDefaultDisplay = isDefaultDisplay;
 
         doReturn(display).when(mStack).getDisplay();
         doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index d54b4a0..f8baf84 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1110,7 +1110,7 @@
         performLayout(mDisplayContent);
 
         // The frame is empty because the requested height is zero.
-        assertTrue(win.getFrameLw().isEmpty());
+        assertTrue(win.getFrame().isEmpty());
         // The window should be scheduled to resize then the client may report a new non-empty size.
         win.updateResizingWindowIfNeeded();
         assertThat(mWm.mResizingWindows).contains(win);
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 0675c6d..2d834ac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -114,7 +114,7 @@
         mWindow = spy(createWindow(null, TYPE_APPLICATION, "window"));
         // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from
         // changing those frames.
-        doNothing().when(mWindow).computeFrameLw();
+        doNothing().when(mWindow).computeFrame();
 
         final WindowManager.LayoutParams attrs = mWindow.mAttrs;
         attrs.width = MATCH_PARENT;
@@ -179,7 +179,7 @@
 
         WindowState win = createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, "StatusBarSubPanel");
         win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR, ITYPE_TOP_GESTURES};
-        win.getFrameLw().set(0, 0, 500, 100);
+        win.getFrame().set(0, 0, 500, 100);
 
         addWindow(win);
         InsetsStateController controller = mDisplayContent.getInsetsStateController();
@@ -207,7 +207,7 @@
         mDisplayPolicy.removeWindowLw(mStatusBarWindow);  // Removes the existing one.
         WindowState win = createWindow(null, TYPE_STATUS_BAR, "StatusBar");
         win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR};
-        win.getFrameLw().set(0, 0, 500, 100);
+        win.getFrame().set(0, 0, 500, 100);
 
         addWindow(win);
         mDisplayContent.getInsetsStateController().onPostLayout();
@@ -232,7 +232,7 @@
         WindowState win1 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel1");
         win1.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
         win1.mAttrs.gravity = Gravity.TOP;
-        win1.getFrameLw().set(0, 0, 200, 500);
+        win1.getFrame().set(0, 0, 200, 500);
         addWindow(win1);
 
         assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_TOP);
@@ -241,7 +241,7 @@
         WindowState win2 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel2");
         win2.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
         win2.mAttrs.gravity = Gravity.BOTTOM;
-        win2.getFrameLw().set(0, 0, 200, 500);
+        win2.getFrame().set(0, 0, 200, 500);
         addWindow(win2);
 
         assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_BOTTOM);
@@ -250,7 +250,7 @@
         WindowState win3 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel3");
         win3.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
         win3.mAttrs.gravity = Gravity.LEFT;
-        win3.getFrameLw().set(0, 0, 200, 500);
+        win3.getFrame().set(0, 0, 200, 500);
         addWindow(win3);
 
         assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_LEFT);
@@ -259,7 +259,7 @@
         WindowState win4 = createWindow(null, TYPE_NAVIGATION_BAR_PANEL, "NavBarPanel4");
         win4.mAttrs.providesInsetsTypes = new int[]{ITYPE_NAVIGATION_BAR};
         win4.mAttrs.gravity = Gravity.RIGHT;
-        win4.getFrameLw().set(0, 0, 200, 500);
+        win4.getFrame().set(0, 0, 200, 500);
         addWindow(win4);
 
         assertEquals(mDisplayPolicy.getAlternateNavBarPosition(), ALT_BAR_RIGHT);
@@ -274,11 +274,11 @@
         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.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDisplayFrame(), 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.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
     }
 
@@ -290,11 +290,11 @@
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
     }
 
@@ -306,11 +306,11 @@
         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, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
     }
 
@@ -322,11 +322,11 @@
         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.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDisplayFrame(), 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.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
     }
 
@@ -342,11 +342,11 @@
         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, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
     }
 
@@ -362,11 +362,11 @@
         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.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDisplayFrame(), 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.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
     }
 
@@ -381,11 +381,11 @@
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0);
+        assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0);
         assertInsetByTopBottom(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0);
-        assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
     }
 
@@ -402,10 +402,10 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
+        assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0);
     }
 
     @Test
@@ -422,10 +422,10 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
+        assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, 0);
     }
 
     @Test
@@ -442,10 +442,10 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
-        assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
         assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0);
     }
 
     @Test
@@ -462,10 +462,10 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
-        assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
         assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0);
     }
 
     @Test
@@ -484,10 +484,10 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
+        assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0);
     }
 
     @Test
@@ -506,10 +506,10 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetByTopBottom(mWindow.getParentFrame(), STATUS_BAR_HEIGHT, 0);
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, 0);
+        assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, 0);
     }
 
     @Test
@@ -529,10 +529,10 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetByTopBottom(mWindow.getParentFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetByTopBottom(mWindow.getDecorFrame(), 0, 0);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), 0, 0);
+        assertInsetByTopBottom(mWindow.getDisplayFrame(), 0, 0);
     }
 
 
@@ -549,11 +549,11 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mWindow.getContentFrameLw(),
+        assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mWindow.getContentFrame(),
                 DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
         assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
     }
 
     @Test
@@ -570,11 +570,11 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetBy(mWindow.getParentFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
-        assertInsetBy(mWindow.getStableFrameLw(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
-        assertInsetBy(mWindow.getContentFrameLw(),
+        assertInsetBy(mWindow.getStableFrame(), NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, 0, 0);
+        assertInsetBy(mWindow.getContentFrame(),
                 NAV_BAR_HEIGHT, STATUS_BAR_HEIGHT, DISPLAY_CUTOUT_HEIGHT, 0);
         assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
+        assertInsetBy(mWindow.getDisplayFrame(), 0, 0, DISPLAY_CUTOUT_HEIGHT, 0);
     }
 
     @Test
@@ -594,8 +594,8 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mWindow.getContentFrameLw(),
+        assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mWindow.getContentFrame(),
                 DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
         assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
     }
@@ -615,7 +615,7 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetByTopBottom(mWindow.getParentFrame(), 0, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getDisplayFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getDisplayFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
     }
 
     @Test
@@ -636,8 +636,8 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
-        assertInsetBy(mWindow.getContentFrameLw(),
+        assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
+        assertInsetBy(mWindow.getContentFrame(),
                 DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT, 0);
         assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
     }
@@ -655,11 +655,11 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
-        assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
+        assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrame(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
                 NAV_BAR_HEIGHT);
         assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
     }
 
     @Test
@@ -676,12 +676,12 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0,
+        assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, 0,
                 NAV_BAR_HEIGHT);
-        assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
+        assertInsetBy(mWindow.getContentFrame(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
                 NAV_BAR_HEIGHT);
         assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
     }
 
     @Test
@@ -698,11 +698,11 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetBy(mWindow.getParentFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
-        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
-        assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
+        assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrame(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
                 NAV_BAR_HEIGHT);
         assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getDisplayFrameLw(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrame(), DISPLAY_CUTOUT_HEIGHT, 0, 0, 0);
     }
 
     @Test
@@ -719,11 +719,11 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getStableFrameLw(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
-        assertInsetBy(mWindow.getContentFrameLw(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
+        assertInsetBy(mWindow.getStableFrame(), 0, STATUS_BAR_HEIGHT, 0, NAV_BAR_HEIGHT);
+        assertInsetBy(mWindow.getContentFrame(), DISPLAY_CUTOUT_HEIGHT, STATUS_BAR_HEIGHT, 0,
                 NAV_BAR_HEIGHT);
         assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0);
     }
 
     @Test
@@ -740,11 +740,11 @@
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
 
         assertInsetBy(mWindow.getParentFrame(), 0, 0, 0, 0);
-        assertInsetByTopBottom(mWindow.getStableFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
-        assertInsetByTopBottom(mWindow.getVisibleFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getStableFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getVisibleFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
         assertInsetBy(mWindow.getDecorFrame(), 0, 0, 0, 0);
-        assertInsetBy(mWindow.getDisplayFrameLw(), 0, 0, 0, 0);
+        assertInsetBy(mWindow.getDisplayFrame(), 0, 0, 0, 0);
     }
 
     @Test
@@ -904,34 +904,34 @@
         updateDecorWindow(decorWindow, MATCH_PARENT, DECOR_WINDOW_INSET, TOP);
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), DECOR_WINDOW_INSET, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), DECOR_WINDOW_INSET, NAV_BAR_HEIGHT);
 
         // Decor on bottom
         updateDecorWindow(decorWindow, MATCH_PARENT, DECOR_WINDOW_INSET, BOTTOM);
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT,
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT,
                 DECOR_WINDOW_INSET);
 
         // Decor on the left
         updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, MATCH_PARENT, LEFT);
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-        assertInsetBy(mWindow.getContentFrameLw(), DECOR_WINDOW_INSET, STATUS_BAR_HEIGHT, 0,
+        assertInsetBy(mWindow.getContentFrame(), DECOR_WINDOW_INSET, STATUS_BAR_HEIGHT, 0,
                 NAV_BAR_HEIGHT);
 
         // Decor on the right
         updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, MATCH_PARENT, RIGHT);
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-        assertInsetBy(mWindow.getContentFrameLw(), 0, STATUS_BAR_HEIGHT, DECOR_WINDOW_INSET,
+        assertInsetBy(mWindow.getContentFrame(), 0, STATUS_BAR_HEIGHT, DECOR_WINDOW_INSET,
                 NAV_BAR_HEIGHT);
 
         // Decor not allowed as inset
         updateDecorWindow(decorWindow, DECOR_WINDOW_INSET, DECOR_WINDOW_INSET, TOP);
         mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
         mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
-        assertInsetByTopBottom(mWindow.getContentFrameLw(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mWindow.getContentFrame(), STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
     }
 
     private void updateDecorWindow(WindowState decorWindow, int width, int height, int gravity) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 4483f8c..b50530e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -297,7 +297,7 @@
 
         final WindowState navigationBar = createNavigationBarWindow();
 
-        navigationBar.getFrameLw().set(new Rect(100, 200, 200, 300));
+        navigationBar.getFrame().set(new Rect(100, 200, 200, 300));
 
         assertFalse("Freeform is overlapping with navigation bar",
                 DisplayPolicy.isOverlappingWithNavBar(targetWin, navigationBar));
@@ -377,7 +377,7 @@
 
         mDisplayContent.setInputMethodWindowLocked(mImeWindow);
         mImeWindow.mAttrs.setFitInsetsSides(Side.all() & ~Side.BOTTOM);
-        mImeWindow.getGivenContentInsetsLw().set(0, displayInfo.logicalHeight, 0, 0);
+        mImeWindow.mGivenContentInsets.set(0, displayInfo.logicalHeight, 0, 0);
         mImeWindow.getControllableInsetProvider().setServerVisible(true);
 
         displayPolicy.beginLayoutLw(mDisplayContent.mDisplayFrames, 0 /* UI mode */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
index 1d6dd0b..a0fa936 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java
@@ -61,7 +61,7 @@
     @Test
     public void testPostLayout() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
-        statusBar.getFrameLw().set(0, 0, 500, 100);
+        statusBar.getFrame().set(0, 0, 500, 100);
         statusBar.mHasSurface = true;
         mProvider.setWindow(statusBar, null, null);
         mProvider.onPostLayout();
@@ -76,9 +76,9 @@
     @Test
     public void testPostLayout_givenInsets() {
         final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
-        ime.getFrameLw().set(0, 0, 500, 100);
-        ime.getGivenContentInsetsLw().set(0, 0, 0, 60);
-        ime.getGivenVisibleInsetsLw().set(0, 0, 0, 75);
+        ime.getFrame().set(0, 0, 500, 100);
+        ime.mGivenContentInsets.set(0, 0, 0, 60);
+        ime.mGivenVisibleInsets.set(0, 0, 0, 75);
         ime.mHasSurface = true;
         mProvider.setWindow(ime, null, null);
         mProvider.onPostLayout();
@@ -94,7 +94,7 @@
     @Test
     public void testPostLayout_invisible() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
-        statusBar.getFrameLw().set(0, 0, 500, 100);
+        statusBar.getFrame().set(0, 0, 500, 100);
         mProvider.setWindow(statusBar, null, null);
         mProvider.onPostLayout();
         assertEquals(Insets.NONE, mProvider.getSource().calculateInsets(new Rect(0, 0, 500, 500),
@@ -104,7 +104,7 @@
     @Test
     public void testPostLayout_frameProvider() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
-        statusBar.getFrameLw().set(0, 0, 500, 100);
+        statusBar.getFrame().set(0, 0, 500, 100);
         statusBar.mHasSurface = true;
         mProvider.setWindow(statusBar,
                 (displayFrames, windowState, rect) -> {
@@ -118,7 +118,7 @@
     public void testUpdateControlForTarget() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
-        statusBar.getFrameLw().set(0, 0, 500, 100);
+        statusBar.getFrame().set(0, 0, 500, 100);
 
         // We must not have control or control target before we have the insets source window.
         mProvider.updateControlForTarget(target, true /* force */);
@@ -163,7 +163,7 @@
     public void testUpdateControlForFakeTarget() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
-        statusBar.getFrameLw().set(0, 0, 500, 100);
+        statusBar.getFrame().set(0, 0, 500, 100);
         mProvider.setWindow(statusBar, null, null);
         mProvider.updateControlForFakeTarget(target);
         assertNotNull(mProvider.getControl(target));
@@ -176,7 +176,7 @@
     public void testUpdateSourceFrameForIme() {
         final WindowState inputMethod = createWindow(null, TYPE_INPUT_METHOD, "inputMethod");
 
-        inputMethod.getFrameLw().set(new Rect(0, 400, 500, 500));
+        inputMethod.getFrame().set(new Rect(0, 400, 500, 500));
 
         mImeProvider.setWindow(inputMethod, null, null);
         mImeProvider.setServerVisible(false);
@@ -190,7 +190,7 @@
         mImeProvider.setServerVisible(true);
         mImeSource.setVisible(true);
         mImeProvider.updateSourceFrame();
-        assertEquals(inputMethod.getFrameLw(), mImeSource.getFrame());
+        assertEquals(inputMethod.getFrame(), mImeSource.getFrame());
         insets = mImeSource.calculateInsets(new Rect(0, 0, 500, 500),
                 false /* ignoreVisibility */);
         assertEquals(Insets.of(0, 0, 0, 100), insets);
@@ -200,7 +200,7 @@
     public void testInsetsModified() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
-        statusBar.getFrameLw().set(0, 0, 500, 100);
+        statusBar.getFrame().set(0, 0, 500, 100);
         mProvider.setWindow(statusBar, null, null);
         mProvider.updateControlForTarget(target, false /* force */);
         InsetsState state = new InsetsState();
@@ -213,7 +213,7 @@
     public void testInsetsModified_noControl() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
         final WindowState target = createWindow(null, TYPE_APPLICATION, "target");
-        statusBar.getFrameLw().set(0, 0, 500, 100);
+        statusBar.getFrame().set(0, 0, 500, 100);
         mProvider.setWindow(statusBar, null, null);
         InsetsState state = new InsetsState();
         state.getSource(ITYPE_STATUS_BAR).setVisible(false);
@@ -224,7 +224,7 @@
     @Test
     public void testInsetGeometries() {
         final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
-        statusBar.getFrameLw().set(0, 0, 500, 100);
+        statusBar.getFrame().set(0, 0, 500, 100);
         statusBar.mHasSurface = true;
         mProvider.setWindow(statusBar, null, null);
         mProvider.onPostLayout();
@@ -236,7 +236,7 @@
                         false /* ignoreVisibility */));
 
         // Don't apply left insets if window is left-of inset-window but still overlaps
-        statusBar.getFrameLw().set(100, 0, 0, 0);
+        statusBar.getFrame().set(100, 0, 0, 0);
         assertEquals(Insets.of(0, 0, 0, 0),
                 mProvider.getSource().calculateInsets(new Rect(-100, 0, 400, 500),
                         false /* ignoreVisibility */));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index b89d168..26b0bfb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -915,24 +915,6 @@
         assertEquals(taskDisplayArea.getTopStack(), taskDisplayArea.getRootHomeTask());
     }
 
-    @Test
-    public void testResumeFocusedStackOnSleepingDisplay() {
-        // Create an activity on secondary display.
-        final TestDisplayContent secondDisplay = addNewDisplayContentAt(
-                DisplayContent.POSITION_TOP);
-        final Task stack = secondDisplay.getDefaultTaskDisplayArea()
-                .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setStack(stack).build();
-        spyOn(activity);
-        spyOn(stack);
-
-        // Cannot resumed activities on secondary display if the display should sleep.
-        doReturn(true).when(secondDisplay).shouldSleep();
-        mRootWindowContainer.resumeFocusedStacksTopActivities();
-        verify(stack, never()).resumeTopActivityUncheckedLocked(any(), any());
-        verify(activity, never()).makeActiveIfNeeded(any());
-    }
-
     /**
      * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
      * info for test cases.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index 72899e7..191c33c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -184,7 +184,7 @@
         };
         activities[0].detachFromProcess();
         activities[1].finishing = true;
-        activities[1].destroyImmediately(true /* removeFromApp */, "test");
+        activities[1].destroyImmediately("test");
         spyOn(wpc);
         doReturn(true).when(wpc).isRemoved();
 
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 0fe6510..08537a4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -432,6 +432,31 @@
     }
 
     @Test
+    public void testComputeConfigResourceLayoutOverrides() {
+        final Rect fullScreenBounds = new Rect(0, 0, 1000, 2500);
+        TestDisplayContent display = new TestDisplayContent.Builder(
+                mAtm, fullScreenBounds.width(), fullScreenBounds.height()).build();
+        final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+        final Configuration inOutConfig = new Configuration();
+        final Configuration parentConfig = new Configuration();
+        final Rect nonLongBounds = new Rect(0, 0, 1000, 1250);
+        parentConfig.windowConfiguration.setBounds(fullScreenBounds);
+        parentConfig.windowConfiguration.setAppBounds(fullScreenBounds);
+        parentConfig.densityDpi = 400;
+        parentConfig.screenHeightDp = (fullScreenBounds.bottom * 160) / parentConfig.densityDpi;
+        parentConfig.screenWidthDp = (fullScreenBounds.right * 160) / parentConfig.densityDpi;
+        parentConfig.windowConfiguration.setRotation(ROTATION_0);
+
+        // Set BOTH screenW/H to an override value
+        inOutConfig.screenWidthDp = nonLongBounds.width() * 160 / parentConfig.densityDpi;
+        inOutConfig.screenHeightDp = nonLongBounds.height() * 160 / parentConfig.densityDpi;
+        task.computeConfigResourceOverrides(inOutConfig, parentConfig);
+
+        // screenLayout should honor override when both screenW/H are set.
+        assertTrue((inOutConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_NO) != 0);
+    }
+
+    @Test
     public void testComputeNestedConfigResourceOverrides() {
         final Task task = new TaskBuilder(mSupervisor).build();
         assertTrue(task.getResolvedOverrideBounds().isEmpty());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
index bce1142..ca3f815 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackChangedListenerTest.java
@@ -53,7 +53,6 @@
 import androidx.test.filters.MediumTest;
 
 import com.android.compatibility.common.util.SystemUtil;
-import com.android.internal.annotations.GuardedBy;
 
 import org.junit.After;
 import org.junit.Before;
@@ -76,14 +75,10 @@
 
     private static final int WAIT_TIMEOUT_MS = 5000;
     private static final Object sLock = new Object();
-    @GuardedBy("sLock")
-    private static boolean sTaskStackChangedCalled;
-    private static boolean sActivityBResumed;
 
     @Before
     public void setUp() throws Exception {
         mService = ActivityManager.getService();
-        sTaskStackChangedCalled = false;
     }
 
     @After
@@ -94,47 +89,33 @@
 
     @Test
     @Presubmit
-    @FlakyTest(bugId = 130388819)
     public void testTaskStackChanged_afterFinish() throws Exception {
+        final TestActivity activity = startTestActivity(ActivityA.class);
+        final CountDownLatch latch = new CountDownLatch(1);
         registerTaskStackChangedListener(new TaskStackListener() {
             @Override
             public void onTaskStackChanged() throws RemoteException {
-                synchronized (sLock) {
-                    sTaskStackChangedCalled = true;
-                }
+                latch.countDown();
             }
         });
 
-        Context context = getInstrumentation().getContext();
-        context.startActivity(
-                new Intent(context, ActivityA.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
-        UiDevice.getInstance(getInstrumentation()).waitForIdle();
-        synchronized (sLock) {
-            assertTrue(sTaskStackChangedCalled);
-        }
-        assertTrue(sActivityBResumed);
+        activity.finish();
+        waitForCallback(latch);
     }
 
     @Test
     @Presubmit
     public void testTaskStackChanged_resumeWhilePausing() throws Exception {
+        final CountDownLatch latch = new CountDownLatch(1);
         registerTaskStackChangedListener(new TaskStackListener() {
             @Override
             public void onTaskStackChanged() throws RemoteException {
-                synchronized (sLock) {
-                    sTaskStackChangedCalled = true;
-                }
+                latch.countDown();
             }
         });
 
-        final Context context = getInstrumentation().getContext();
-        context.startActivity(new Intent(context, ResumeWhilePausingActivity.class).addFlags(
-                Intent.FLAG_ACTIVITY_NEW_TASK));
-        UiDevice.getInstance(getInstrumentation()).waitForIdle();
-
-        synchronized (sLock) {
-            assertTrue(sTaskStackChangedCalled);
-        }
+        startTestActivity(ResumeWhilePausingActivity.class);
+        waitForCallback(latch);
     }
 
     @Test
@@ -512,7 +493,7 @@
         try {
             final boolean result = latch.await(WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
             if (!result) {
-                throw new RuntimeException("Timed out waiting for task stack change notification");
+                throw new AssertionError("Timed out waiting for task stack change notification");
             }
         } catch (InterruptedException e) {
         }
@@ -569,19 +550,6 @@
     }
 
     public static class ActivityA extends TestActivity {
-
-        private boolean mActivityBLaunched = false;
-
-        @Override
-        protected void onPostResume() {
-            super.onPostResume();
-            if (mActivityBLaunched) {
-                return;
-            }
-            mActivityBLaunched = true;
-            finish();
-            startActivity(new Intent(this, ActivityB.class));
-        }
     }
 
     public static class ActivityB extends TestActivity {
@@ -589,10 +557,6 @@
         @Override
         protected void onPostResume() {
             super.onPostResume();
-            synchronized (sLock) {
-                sTaskStackChangedCalled = false;
-            }
-            sActivityBResumed = true;
             finish();
         }
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 4a8e8da..dc85904 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -86,11 +86,6 @@
     }
 
     @Override
-    public int getMaxWallpaperLayer() {
-        return 0;
-    }
-
-    @Override
     public boolean isKeyguardHostWindow(WindowManager.LayoutParams attrs) {
         return attrs.type == TYPE_NOTIFICATION_SHADE;
     }
@@ -377,11 +372,6 @@
     }
 
     @Override
-    public boolean isTopLevelWindow(int windowType) {
-        return false;
-    }
-
-    @Override
     public void startKeyguardExitAnimation(long startTime, long fadeoutDuration) {
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
index ed9e270..63367ac 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java
@@ -133,8 +133,8 @@
         int expectedWidth = (int) (wallpaperWidth * (displayHeight / (double) wallpaperHeight));
 
         // Check that the wallpaper is correctly scaled
-        assertEquals(new Rect(0, 0, expectedWidth, displayHeight), wallpaperWindow.getFrameLw());
-        Rect portraitFrame = wallpaperWindow.getFrameLw();
+        assertEquals(new Rect(0, 0, expectedWidth, displayHeight), wallpaperWindow.getFrame());
+        Rect portraitFrame = wallpaperWindow.getFrame();
 
         // Rotate the display
         dc.getDisplayRotation().updateOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, true);
@@ -149,7 +149,7 @@
 
         // Check that the wallpaper has the same frame in landscape than in portrait
         assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getConfiguration().orientation);
-        assertEquals(portraitFrame, wallpaperWindow.getFrameLw());
+        assertEquals(portraitFrame, wallpaperWindow.getFrame());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index eb2aa41..ca3626d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -89,29 +89,29 @@
     }
 
     private void assertFrame(WindowState w, Rect frame) {
-        assertEquals(w.getFrameLw(), frame);
+        assertEquals(w.getFrame(), frame);
     }
 
     private void assertFrame(WindowState w, int left, int top, int right, int bottom) {
-        assertRect(w.getFrameLw(), left, top, right, bottom);
+        assertRect(w.getFrame(), left, top, right, bottom);
     }
 
     private void assertRelFrame(WindowState w, int left, int top, int right, int bottom) {
-        assertRect(w.getRelativeFrameLw(), left, top, right, bottom);
+        assertRect(w.getRelativeFrame(), left, top, right, bottom);
     }
 
     private void assertContentFrame(WindowState w, Rect expectedRect) {
-        assertRect(w.getContentFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right,
+        assertRect(w.getContentFrame(), expectedRect.left, expectedRect.top, expectedRect.right,
                 expectedRect.bottom);
     }
 
     private void assertVisibleFrame(WindowState w, Rect expectedRect) {
-        assertRect(w.getVisibleFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right,
+        assertRect(w.getVisibleFrame(), expectedRect.left, expectedRect.top, expectedRect.right,
                 expectedRect.bottom);
     }
 
     private void assertStableFrame(WindowState w, Rect expectedRect) {
-        assertRect(w.getStableFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right,
+        assertRect(w.getStableFrame(), expectedRect.left, expectedRect.top, expectedRect.right,
                 expectedRect.bottom);
     }
 
@@ -155,7 +155,7 @@
         // the difference between mFrame and ContentFrame. Visible
         // and stable frames work the same way.
         w.getWindowFrames().setFrames(pf, df, cf, vf, dcf, sf);
-        w.computeFrameLw();
+        w.computeFrame();
         assertFrame(w, 0, 0, 1000, 1000);
         assertRelFrame(w, 0, 0, 1000, 1000);
         assertContentInset(w, 0, topContentInset, 0, bottomContentInset);
@@ -170,14 +170,14 @@
         w.mAttrs.width = 100; w.mAttrs.height = 100; //have to clear MATCH_PARENT
         w.mRequestedWidth = 100;
         w.mRequestedHeight = 100;
-        w.computeFrameLw();
+        w.computeFrame();
         assertFrame(w, 100, 100, 200, 200);
         assertRelFrame(w, 100, 100, 200, 200);
         assertContentInset(w, 0, 0, 0, 0);
         // In this case the frames are shrunk to the window frame.
-        assertContentFrame(w, w.getFrameLw());
-        assertVisibleFrame(w, w.getFrameLw());
-        assertStableFrame(w, w.getFrameLw());
+        assertContentFrame(w, w.getFrame());
+        assertVisibleFrame(w, w.getFrame());
+        assertStableFrame(w, w.getFrame());
     }
 
     @Test
@@ -193,7 +193,7 @@
         // Here the window has FILL_PARENT, FILL_PARENT
         // so we expect it to fill the entire available frame.
         w.getWindowFrames().setFrames(pf, pf, pf, pf, pf, pf);
-        w.computeFrameLw();
+        w.computeFrame();
         assertFrame(w, 0, 0, 1000, 1000);
         assertRelFrame(w, 0, 0, 1000, 1000);
 
@@ -202,14 +202,14 @@
         // and we use mRequestedWidth/mRequestedHeight
         w.mAttrs.width = 300;
         w.mAttrs.height = 300;
-        w.computeFrameLw();
+        w.computeFrame();
         // Explicit width and height without requested width/height
         // gets us nothing.
         assertFrame(w, 0, 0, 0, 0);
 
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
-        w.computeFrameLw();
+        w.computeFrame();
         // With requestedWidth/Height we can freely choose our size within the
         // parent bounds.
         assertFrame(w, 0, 0, 300, 300);
@@ -222,14 +222,14 @@
         w.mRequestedWidth = -1;
         w.mAttrs.width = 100;
         w.mAttrs.height = 100;
-        w.computeFrameLw();
+        w.computeFrame();
         assertFrame(w, 0, 0, 100, 100);
         w.mAttrs.flags = 0;
 
         // But sizes too large will be clipped to the containing frame
         w.mRequestedWidth = 1200;
         w.mRequestedHeight = 1200;
-        w.computeFrameLw();
+        w.computeFrame();
         assertFrame(w, 0, 0, 1000, 1000);
 
         // Before they are clipped though windows will be shifted
@@ -237,7 +237,7 @@
         w.mAttrs.y = 300;
         w.mRequestedWidth = 1000;
         w.mRequestedHeight = 1000;
-        w.computeFrameLw();
+        w.computeFrame();
         assertFrame(w, 0, 0, 1000, 1000);
 
         // If there is room to move around in the parent frame the window will be shifted according
@@ -247,18 +247,18 @@
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
-        w.computeFrameLw();
+        w.computeFrame();
         assertFrame(w, 700, 0, 1000, 300);
         assertRelFrame(w, 700, 0, 1000, 300);
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
-        w.computeFrameLw();
+        w.computeFrame();
         assertFrame(w, 700, 700, 1000, 1000);
         assertRelFrame(w, 700, 700, 1000, 1000);
         // Window specified  x and y are interpreted as offsets in the opposite
         // direction of gravity
         w.mAttrs.x = 100;
         w.mAttrs.y = 100;
-        w.computeFrameLw();
+        w.computeFrame();
         assertFrame(w, 600, 600, 900, 900);
         assertRelFrame(w, 600, 600, 900, 900);
     }
@@ -285,12 +285,12 @@
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
         final WindowFrames windowFrames = w.getWindowFrames();
         windowFrames.setFrames(pf, pf, pf, pf, pf, pf);
-        w.computeFrameLw();
+        w.computeFrame();
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
-        assertEquals(resolvedTaskBounds, w.getFrameLw());
-        assertEquals(0, w.getRelativeFrameLw().left);
-        assertEquals(0, w.getRelativeFrameLw().top);
+        assertEquals(resolvedTaskBounds, w.getFrame());
+        assertEquals(0, w.getRelativeFrame().left);
+        assertEquals(0, w.getRelativeFrame().top);
         assertContentFrame(w, resolvedTaskBounds);
         assertContentInset(w, 0, 0, 0, 0);
 
@@ -300,10 +300,10 @@
         final int cfBottom = logicalHeight / 2;
         final Rect cf = new Rect(0, 0, cfRight, cfBottom);
         windowFrames.setFrames(pf, pf, cf, cf, pf, cf);
-        w.computeFrameLw();
-        assertEquals(resolvedTaskBounds, w.getFrameLw());
-        assertEquals(0, w.getRelativeFrameLw().left);
-        assertEquals(0, w.getRelativeFrameLw().top);
+        w.computeFrame();
+        assertEquals(resolvedTaskBounds, w.getFrame());
+        assertEquals(0, w.getRelativeFrame().left);
+        assertEquals(0, w.getRelativeFrame().top);
         int contentInsetRight = resolvedTaskBounds.right - cfRight;
         int contentInsetBottom = resolvedTaskBounds.bottom - cfBottom;
         assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom);
@@ -334,12 +334,12 @@
 
         final WindowFrames windowFrames = w.getWindowFrames();
         windowFrames.setFrames(pf, df, cf, vf, dcf, sf);
-        w.computeFrameLw();
+        w.computeFrame();
         assertPolicyCrop(w, 0, cf.top, logicalWidth, cf.bottom);
 
         windowFrames.mDecorFrame.setEmpty();
         // Likewise with no decor frame we would get no crop
-        w.computeFrameLw();
+        w.computeFrame();
         assertPolicyCrop(w, 0, 0, logicalWidth, logicalHeight);
 
         // Now we set up a window which doesn't fill the entire decor frame.
@@ -353,7 +353,7 @@
         w.mAttrs.height = logicalHeight / 2;
         w.mRequestedWidth = logicalWidth / 2;
         w.mRequestedHeight = logicalHeight / 2;
-        w.computeFrameLw();
+        w.computeFrame();
 
         // Normally the crop is shrunk from the decor frame
         // to the computed window frame.
@@ -390,7 +390,7 @@
         final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight);
         final WindowFrames windowFrames = w.getWindowFrames();
         windowFrames.setFrames(pf, pf, pf, pf, pf, pf);
-        w.computeFrameLw();
+        w.computeFrame();
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
         assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
@@ -408,7 +408,7 @@
         task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         task.setBounds(null);
         windowFrames.setFrames(pf, pf, cf, cf, pf, cf);
-        w.computeFrameLw();
+        w.computeFrame();
         assertFrame(w, cf);
         assertContentFrame(w, cf);
         assertContentInset(w, 0, 0, 0, 0);
@@ -430,7 +430,7 @@
         final WindowFrames windowFrames = w.getWindowFrames();
         windowFrames.setFrames(pf, pf, pf, pf, pf, pf);
         windowFrames.setDisplayCutout(cutout);
-        w.computeFrameLw();
+        w.computeFrame();
 
         assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
         assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
@@ -469,20 +469,20 @@
 
         task.setBounds(winRect);
         w.getWindowFrames().setFrames(pf, df, cf, vf, dcf, sf);
-        w.computeFrameLw();
+        w.computeFrame();
 
         final Rect expected = new Rect(winRect.left, cf.bottom - winRect.height(),
                 winRect.right, cf.bottom);
-        assertEquals(expected, w.getFrameLw());
-        assertEquals(expected, w.getContentFrameLw());
-        assertEquals(expected, w.getVisibleFrameLw());
+        assertEquals(expected, w.getFrame());
+        assertEquals(expected, w.getContentFrame());
+        assertEquals(expected, w.getVisibleFrame());
 
         // Now check that it won't get moved beyond the top and then has appropriate insets
         winRect.bottom = 600;
         task.setBounds(winRect);
         w.setBounds(winRect);
         w.getWindowFrames().setFrames(pf, df, cf, vf, dcf, sf);
-        w.computeFrameLw();
+        w.computeFrame();
 
         assertFrame(w, winRect.left, 0, winRect.right, winRect.height());
         expected.top = 0;
@@ -492,8 +492,8 @@
 
         // Check that it's moved back without ime insets
         w.getWindowFrames().setFrames(pf, df, pf, pf, dcf, sf);
-        w.computeFrameLw();
-        assertEquals(winRect, w.getFrameLw());
+        w.computeFrame();
+        assertEquals(winRect, w.getFrame());
     }
 
     private WindowState createWindow() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 9135297..289d54e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -88,20 +88,14 @@
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class WindowOrganizerTests extends WindowTestsBase {
-    private ITaskOrganizer registerMockOrganizer(int windowingMode) {
+    private ITaskOrganizer registerMockOrganizer() {
         final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
         when(organizer.asBinder()).thenReturn(new Binder());
 
-        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
-                organizer, windowingMode);
-
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer);
         return organizer;
     }
 
-    private ITaskOrganizer registerMockOrganizer() {
-        return registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
-    }
-
     Task createTask(Task stack, boolean fakeDraw) {
         final Task task = createTaskInStack(stack, 0);
 
@@ -133,11 +127,12 @@
         final Task task = createTask(stack);
         final ITaskOrganizer organizer = registerMockOrganizer();
 
-        task.setTaskOrganizer(organizer);
+        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        stack.setTaskOrganizer(organizer);
         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
 
 
-        task.removeImmediately();
+        stack.removeImmediately();
         verify(organizer).onTaskVanished(any());
     }
 
@@ -147,16 +142,17 @@
         final Task task = createTask(stack, false);
         final ITaskOrganizer organizer = registerMockOrganizer();
 
-        task.setTaskOrganizer(organizer);
+        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        stack.setTaskOrganizer(organizer);
 
         verify(organizer, never())
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-        task.setHasBeenVisible(true);
+        stack.setHasBeenVisible(true);
         assertTrue(stack.getHasBeenVisible());
 
         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
 
-        task.removeImmediately();
+        stack.removeImmediately();
         verify(organizer).onTaskVanished(any());
     }
 
@@ -169,42 +165,14 @@
         // In this test we skip making the Task visible, and verify
         // that even though a TaskOrganizer is set remove doesn't emit
         // a vanish callback, because we never emitted appear.
-        task.setTaskOrganizer(organizer);
+        stack.setTaskOrganizer(organizer);
         verify(organizer, never())
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-        task.removeImmediately();
+        stack.removeImmediately();
         verify(organizer, never()).onTaskVanished(any());
     }
 
     @Test
-    public void testSwapOrganizer() throws RemoteException {
-        final Task stack = createStack();
-        final Task task = createTask(stack);
-        final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
-        final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
-
-        task.setTaskOrganizer(organizer);
-        verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-        task.setTaskOrganizer(organizer2);
-        verify(organizer).onTaskVanished(any());
-        verify(organizer2).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-    }
-
-    @Test
-    public void testSwapWindowingModes() throws RemoteException {
-        final Task stack = createStack();
-        final Task task = createTask(stack);
-        final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
-        final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
-
-        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-        stack.setWindowingMode(WINDOWING_MODE_PINNED);
-        verify(organizer).onTaskVanished(any());
-        verify(organizer2).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
-    }
-
-    @Test
     public void testTaskNoDraw() throws RemoteException {
         final Task stack = createStack();
         final Task task = createTask(stack, false /* fakeDraw */);
@@ -226,6 +194,7 @@
         final Task task = createTask(stack);
         final ITaskOrganizer organizer = registerMockOrganizer();
 
+        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         stack.setTaskOrganizer(organizer);
         verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
         assertTrue(stack.isOrganized());
@@ -258,7 +227,7 @@
         final Task task2 = createTask(stack2);
         final Task stack3 = createStack();
         final Task task3 = createTask(stack3);
-        final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
+        final ITaskOrganizer organizer = registerMockOrganizer();
 
         // First organizer is registered, verify a task appears when changing windowing mode
         stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -268,7 +237,7 @@
 
         // Now we replace the registration and1 verify the new organizer receives tasks
         // newly entering the windowing mode.
-        final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
+        final ITaskOrganizer organizer2 = registerMockOrganizer();
         stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         // One each for task and task2
         verify(organizer2, times(2))
@@ -294,7 +263,7 @@
 
     @Test
     public void testRegisterTaskOrganizerStackWindowingModeChanges() throws RemoteException {
-        final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
+        final ITaskOrganizer organizer = registerMockOrganizer();
 
         final Task stack = createStack();
         final Task task = createTask(stack);
@@ -313,7 +282,7 @@
         final Task task = createTask(stack);
         stack.setWindowingMode(WINDOWING_MODE_PINNED);
 
-        final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_PINNED);
+        final ITaskOrganizer organizer = registerMockOrganizer();
         verify(organizer, times(1))
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
     }
@@ -483,8 +452,7 @@
             public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
             }
         };
-        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener,
-                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
         RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
                 mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
 
@@ -542,8 +510,7 @@
             public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
             }
         };
-        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener,
-                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
         RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
                 mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
         lastReportedTiles.clear();
@@ -604,10 +571,7 @@
             public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {
             }
         };
-        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
-                listener, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
-                listener, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener);
         RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
                 mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
         RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
@@ -874,7 +838,7 @@
     @Test
     public void testEnterPipParams() {
         final StubOrganizer o = new StubOrganizer();
-        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o, WINDOWING_MODE_PINNED);
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
         final ActivityRecord record = makePipableActivity();
 
         final PictureInPictureParams p = new PictureInPictureParams.Builder()
@@ -895,7 +859,7 @@
             }
         }
         ChangeSavingOrganizer o = new ChangeSavingOrganizer();
-        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o, WINDOWING_MODE_PINNED);
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
 
         final ActivityRecord record = makePipableActivity();
         final PictureInPictureParams p = new PictureInPictureParams.Builder()
@@ -926,8 +890,7 @@
             }
         }
         ChangeSavingOrganizer o = new ChangeSavingOrganizer();
-        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o,
-                WINDOWING_MODE_MULTI_WINDOW);
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(o);
 
         final Task stack = createStack();
         final Task task = createTask(stack);
@@ -942,22 +905,23 @@
     @Test
     public void testPreventDuplicateAppear() throws RemoteException {
         final Task stack = createStack();
-        final Task task = createTask(stack);
+        final Task task = createTask(stack, false /* fakeDraw */);
         final ITaskOrganizer organizer = registerMockOrganizer();
 
-        task.setTaskOrganizer(organizer);
+        stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        stack.setTaskOrganizer(organizer);
         // setHasBeenVisible was already called once by the set-up code.
-        task.setHasBeenVisible(true);
+        stack.setHasBeenVisible(true);
         verify(organizer, times(1))
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
 
-        task.setTaskOrganizer(null);
+        stack.setTaskOrganizer(null);
         verify(organizer, times(1)).onTaskVanished(any());
-        task.setTaskOrganizer(organizer);
+        stack.setTaskOrganizer(organizer);
         verify(organizer, times(2))
                 .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class));
 
-        task.removeImmediately();
+        stack.removeImmediately();
         verify(organizer, times(2)).onTaskVanished(any());
     }
 
@@ -966,7 +930,7 @@
         final Task stack = createStack();
         final Task task = createTask(stack);
         final ActivityRecord activity = createActivityRecordInTask(stack.mDisplayContent, task);
-        final ITaskOrganizer organizer = registerMockOrganizer(WINDOWING_MODE_MULTI_WINDOW);
+        final ITaskOrganizer organizer = registerMockOrganizer();
 
         // Setup the task to be controlled by the MW mode organizer
         stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index f095fd4..9603d28 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -479,7 +479,7 @@
         app.mHasSurface = true;
         app.mSurfaceControl = mock(SurfaceControl.class);
         try {
-            app.getFrameLw().set(10, 20, 60, 80);
+            app.getFrame().set(10, 20, 60, 80);
             app.updateSurfacePosition(t);
 
             app.seamlesslyRotateIfAllowed(t, ROTATION_0, ROTATION_90, true);
@@ -519,7 +519,7 @@
                 new Rect(95, 378, 105, 400));
         wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400)));
 
-        app.computeFrameLw();
+        app.computeFrame();
         assertThat(app.getWmDisplayCutout().getDisplayCutout(), is(cutout.inset(7, 10, 5, 20)));
     }
 
@@ -633,7 +633,7 @@
         final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
 
         final DisplayContent dc = createNewDisplay();
-        win0.getFrameLw().offsetTo(PARENT_WINDOW_OFFSET, 0);
+        win0.getFrame().offsetTo(PARENT_WINDOW_OFFSET, 0);
         dc.reparentDisplayContent(win0, win0.getSurfaceControl());
         dc.updateLocation(win0, DISPLAY_IN_PARENT_WINDOW_OFFSET, 0);
 
@@ -644,7 +644,7 @@
         win1.mHasSurface = true;
         win1.mSurfaceControl = mock(SurfaceControl.class);
         win1.mAttrs.surfaceInsets.set(1, 2, 3, 4);
-        win1.getFrameLw().offsetTo(WINDOW_OFFSET, 0);
+        win1.getFrame().offsetTo(WINDOW_OFFSET, 0);
         win1.updateSurfacePosition(t);
         win1.getTransformationMatrix(values, matrix);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 5ce61b4..38c4e0a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1031,10 +1031,7 @@
         TestSplitOrganizer(ActivityTaskManagerService service, int displayId) {
             mService = service;
             mDisplayId = displayId;
-            mService.mTaskOrganizerController.registerTaskOrganizer(this,
-                    WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
-            mService.mTaskOrganizerController.registerTaskOrganizer(this,
-                    WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+            mService.mTaskOrganizerController.registerTaskOrganizer(this);
             WindowContainerToken primary = mService.mTaskOrganizerController.createRootTask(
                     displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token;
             mPrimary = WindowContainer.fromBinder(primary.asBinder()).asTask();
@@ -1134,7 +1131,7 @@
         }
 
         @Override
-        public boolean isGoneForLayoutLw() {
+        public boolean isGoneForLayout() {
             return false;
         }
 
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 1e5d92b..81aad97 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1653,8 +1653,8 @@
 
             // If the calling app is asking about itself, continue, else check for permission.
             if (packageName.equals(callingPackage)) {
-                final int actualCallingUid = mPackageManagerInternal.getPackageUidInternal(
-                        callingPackage, 0, userId);
+                final int actualCallingUid = mPackageManagerInternal.getPackageUid(
+                        callingPackage, /* flags= */ 0, userId);
                 if (actualCallingUid != callingUid) {
                     return false;
                 }
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
index 8f1d0ad..3104c7e 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -123,7 +123,7 @@
         try {
             iorap = IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd"));
         } catch (ServiceManager.ServiceNotFoundException e) {
-            handleRemoteError(e);
+            Log.w(TAG, e.getMessage());
             return null;
         }
 
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index 52e0953..944edd5 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -687,10 +687,8 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAnyRadioPoweredOn();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApnMetered(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isApplicationOnUicc(int);
-    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataConnectionAllowed();
     method public boolean isDataConnectivityPossible();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
-    method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledWithReason(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
     method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
@@ -722,7 +720,6 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledWithReason(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultiSimCarrierRestriction(boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean);
@@ -760,10 +757,6 @@
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
     field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
-    field public static final int DATA_ENABLED_REASON_CARRIER = 2; // 0x2
-    field public static final int DATA_ENABLED_REASON_POLICY = 1; // 0x1
-    field public static final int DATA_ENABLED_REASON_THERMAL = 3; // 0x3
-    field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
     field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
     field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
     field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index fa229fb..a229efb 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4406,7 +4406,7 @@
                 });
         sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
         sDefaults.putAll(Ims.getDefaults());
-        sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null);
+        sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, new String[0]);
          sDefaults.putBoolean(KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL, false);
         sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
                 new int[] {4 /* BUSY */});
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index 06c34dc..905f908 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -50,6 +50,18 @@
     // a list of additional PLMN-IDs reported for this cell
     private final ArraySet<String> mAdditionalPlmns;
 
+    /** @hide */
+    public CellIdentityNr() {
+        super(TAG, CellInfo.TYPE_NR, null, null, null, null);
+        mNrArfcn = CellInfo.UNAVAILABLE;
+        mPci = CellInfo.UNAVAILABLE;
+        mTac = CellInfo.UNAVAILABLE;
+        mNci = CellInfo.UNAVAILABLE;
+        mBands = new int[] {};
+        mAdditionalPlmns = new ArraySet();
+        mGlobalCellId = null;
+    }
+
     /**
      *
      * @param pci Physical Cell Id in range [0, 1007].
diff --git a/telephony/java/android/telephony/CellInfoNr.java b/telephony/java/android/telephony/CellInfoNr.java
index a7e79f9..e01e8f0 100644
--- a/telephony/java/android/telephony/CellInfoNr.java
+++ b/telephony/java/android/telephony/CellInfoNr.java
@@ -29,9 +29,16 @@
 public final class CellInfoNr extends CellInfo {
     private static final String TAG = "CellInfoNr";
 
-    private final CellIdentityNr mCellIdentity;
+    private CellIdentityNr mCellIdentity;
     private final CellSignalStrengthNr mCellSignalStrength;
 
+    /** @hide */
+    public CellInfoNr() {
+        super();
+        mCellIdentity = new CellIdentityNr();
+        mCellSignalStrength = new CellSignalStrengthNr();
+    }
+
     private CellInfoNr(Parcel in) {
         super(in);
         mCellIdentity = CellIdentityNr.CREATOR.createFromParcel(in);
@@ -71,6 +78,11 @@
         return mCellIdentity;
     }
 
+    /** @hide */
+    public void setCellIdentity(CellIdentityNr cid) {
+        mCellIdentity = cid;
+    }
+
     /**
      * @return a {@link CellSignalStrengthNr} instance.
      */
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index f9148d0..7a77922 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -9231,7 +9231,7 @@
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @param enable Whether to enable mobile data.
-     * @deprecated use setDataEnabledWithReason with reason DATA_ENABLED_REASON_USER instead.
+     * @deprecated use setDataEnabledForReason with reason DATA_ENABLED_REASON_USER instead.
      *
      */
     @Deprecated
@@ -9243,16 +9243,16 @@
 
     /**
      * @hide
-     * @deprecated use {@link #setDataEnabledWithReason(int, boolean)} instead.
+     * @deprecated use {@link #setDataEnabledForReason(int, boolean)} instead.
     */
     @SystemApi
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setDataEnabled(int subId, boolean enable) {
         try {
-            setDataEnabledWithReason(subId, DATA_ENABLED_REASON_USER, enable);
+            setDataEnabledForReason(subId, DATA_ENABLED_REASON_USER, enable);
         } catch (RuntimeException e) {
-            Log.e(TAG, "Error calling setDataEnabledWithReason e:" + e);
+            Log.e(TAG, "Error calling setDataEnabledForReason e:" + e);
         }
     }
 
@@ -9461,9 +9461,9 @@
     @SystemApi
     public boolean getDataEnabled(int subId) {
         try {
-            return isDataEnabledWithReason(DATA_ENABLED_REASON_USER);
+            return isDataEnabledForReason(DATA_ENABLED_REASON_USER);
         } catch (RuntimeException e) {
-            Log.e(TAG, "Error calling isDataEnabledWithReason e:" + e);
+            Log.e(TAG, "Error calling isDataEnabledForReason e:" + e);
         }
         return false;
     }
@@ -11016,7 +11016,7 @@
      *
      * @param enabled control enable or disable carrier data.
      * @see #resetAllCarrierActions()
-     * @deprecated use {@link #setDataEnabledWithReason(int, boolean) with
+     * @deprecated use {@link #setDataEnabledForReason(int, boolean) with
      * reason {@link #DATA_ENABLED_REASON_CARRIER}} instead.
      * @hide
      */
@@ -11025,9 +11025,9 @@
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setCarrierDataEnabled(boolean enabled) {
         try {
-            setDataEnabledWithReason(DATA_ENABLED_REASON_CARRIER, enabled);
+            setDataEnabledForReason(DATA_ENABLED_REASON_CARRIER, enabled);
         } catch (RuntimeException e) {
-            Log.e(TAG, "Error calling setDataEnabledWithReason e:" + e);
+            Log.e(TAG, "Error calling setDataEnabledForReason e:" + e);
         }
     }
 
@@ -11113,7 +11113,7 @@
     /**
      * Policy control of data connection. Usually used when data limit is passed.
      * @param enabled True if enabling the data, otherwise disabling.
-     * @deprecated use {@link #setDataEnabledWithReason(int, boolean) with
+     * @deprecated use {@link #setDataEnabledForReason(int, boolean) with
      * reason {@link #DATA_ENABLED_REASON_POLICY}} instead.
      * @hide
      */
@@ -11121,9 +11121,9 @@
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
     public void setPolicyDataEnabled(boolean enabled) {
         try {
-            setDataEnabledWithReason(DATA_ENABLED_REASON_POLICY, enabled);
+            setDataEnabledForReason(DATA_ENABLED_REASON_POLICY, enabled);
         } catch (RuntimeException e) {
-            Log.e(TAG, "Error calling setDataEnabledWithReason e:" + e);
+            Log.e(TAG, "Error calling setDataEnabledForReason e:" + e);
         }
     }
 
@@ -11139,36 +11139,28 @@
 
     /**
      * To indicate that user enabled or disabled data.
-     * @hide
      */
-    @SystemApi
     public static final int DATA_ENABLED_REASON_USER = 0;
 
     /**
      * To indicate that data control due to policy. Usually used when data limit is passed.
      * Policy data on/off won't affect user settings but will bypass the
      * settings and turns off data internally if set to {@code false}.
-     * @hide
      */
-    @SystemApi
     public static final int DATA_ENABLED_REASON_POLICY = 1;
 
     /**
      * To indicate enable or disable carrier data by the system based on carrier signalling or
      * carrier privileged apps. Carrier data on/off won't affect user settings but will bypass the
      * settings and turns off data internally if set to {@code false}.
-     * @hide
      */
-    @SystemApi
     public static final int DATA_ENABLED_REASON_CARRIER = 2;
 
     /**
      * To indicate enable or disable data by thermal service.
      * Thermal data on/off won't affect user settings but will bypass the
      * settings and turns off data internally if set to {@code false}.
-     * @hide
      */
-    @SystemApi
     public static final int DATA_ENABLED_REASON_THERMAL = 3;
 
     /**
@@ -11197,25 +11189,23 @@
      * has {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} irrespective of
      * the reason.
      * @throws IllegalStateException if the Telephony process is not currently available.
-     * @hide
      */
     @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
-    @SystemApi
-    public void setDataEnabledWithReason(@DataEnabledReason int reason, boolean enabled) {
-        setDataEnabledWithReason(getSubId(), reason, enabled);
+    public void setDataEnabledForReason(@DataEnabledReason int reason, boolean enabled) {
+        setDataEnabledForReason(getSubId(), reason, enabled);
     }
 
-    private void setDataEnabledWithReason(int subId, @DataEnabledReason int reason,
+    private void setDataEnabledForReason(int subId, @DataEnabledReason int reason,
             boolean enabled) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                service.setDataEnabledWithReason(subId, reason, enabled);
+                service.setDataEnabledForReason(subId, reason, enabled);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            Log.e(TAG, "Telephony#setDataEnabledWithReason RemoteException", ex);
+            Log.e(TAG, "Telephony#setDataEnabledForReason RemoteException", ex);
             ex.rethrowFromSystemServer();
         }
     }
@@ -11223,9 +11213,11 @@
     /**
      * Return whether data is enabled for certain reason .
      *
-     * If {@link #isDataEnabledWithReason} returns false, it means in data enablement for a
+     * If {@link #isDataEnabledForReason} returns false, it means in data enablement for a
      * specific reason is turned off. If any of the reason is off, then it will result in
-     * bypassing user preference and result in data to be turned off.
+     * bypassing user preference and result in data to be turned off. Call
+     * {@link #isDataConnectionAllowed} in order to know whether
+     * data connection is allowed on the device.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies
      *      to the given subId. Otherwise, applies to
@@ -11234,27 +11226,26 @@
      * @param reason the reason the data enable change is taking place
      * @return whether data is enabled for a reason.
      * <p>Requires Permission:
-     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}
      * @throws IllegalStateException if the Telephony process is not currently available.
-     * @hide
      */
     @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
             android.Manifest.permission.READ_PHONE_STATE})
-    @SystemApi
-    public boolean isDataEnabledWithReason(@DataEnabledReason int reason) {
-        return isDataEnabledWithReason(getSubId(), reason);
+    public boolean isDataEnabledForReason(@DataEnabledReason int reason) {
+        return isDataEnabledForReason(getSubId(), reason);
     }
 
-    private boolean isDataEnabledWithReason(int subId, @DataEnabledReason int reason) {
+    private boolean isDataEnabledForReason(int subId, @DataEnabledReason int reason) {
         try {
             ITelephony service = getITelephony();
             if (service != null) {
-                return service.isDataEnabledWithReason(subId, reason);
+                return service.isDataEnabledForReason(subId, reason);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            Log.e(TAG, "Telephony#isDataEnabledWithReason RemoteException", ex);
+            Log.e(TAG, "Telephony#isDataEnabledForReason RemoteException", ex);
             ex.rethrowFromSystemServer();
         }
         return false;
@@ -11395,10 +11386,14 @@
      *   <LI>And possibly others.</LI>
      * </UL>
      * @return {@code true} if the overall data connection is allowed; {@code false} if not.
-     * @hide
+     * <p>Requires Permission:
+     * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} or
+     * android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE
      */
-    @SystemApi
-    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
+            android.Manifest.permission.READ_PHONE_STATE,
+            android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE})
     public boolean isDataConnectionAllowed() {
         boolean retVal = false;
         try {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index e2de5c8..4021d0a 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1015,7 +1015,7 @@
      * @param reason the reason the data enable change is taking place
      * @param enable true to turn on, else false
      */
-     void setDataEnabledWithReason(int subId, int reason, boolean enable);
+     void setDataEnabledForReason(int subId, int reason, boolean enable);
 
     /**
      * Return whether data is enabled for certain reason
@@ -1023,7 +1023,7 @@
      * @param reason the reason the data enable change is taking place
      * @return true on enabled
     */
-    boolean isDataEnabledWithReason(int subId, int reason);
+    boolean isDataEnabledForReason(int subId, int reason);
 
      /**
      * Checks if manual network selection is allowed.
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 7790043..05a59ef 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -744,6 +744,15 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="BlurActivity"
+                  android:label="Shaders/Blur"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="com.android.test.hwui.TEST"/>
+            </intent-filter>
+        </activity>
+
         <activity android:name="TextActivity"
              android:label="Text/Simple Text"
              android:theme="@android:style/Theme.NoTitleBar"
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
new file mode 100644
index 0000000..033fb0e
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BlurActivity.java
@@ -0,0 +1,110 @@
+/*
+ * 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.test.hwui;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.BlurShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BlurActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        LinearLayout layout = new LinearLayout(this);
+        layout.setClipChildren(false);
+        layout.setGravity(Gravity.CENTER);
+        layout.setOrientation(LinearLayout.VERTICAL);
+
+
+        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(500, 500);
+        params.bottomMargin = 100;
+
+        layout.addView(new BlurGradientView(this), params);
+        layout.addView(new BlurView(this), params);
+
+        setContentView(layout);
+    }
+
+    public static class BlurGradientView extends View {
+        private BlurShader mBlurShader = null;
+        private Paint mPaint;
+
+        public BlurGradientView(Context c) {
+            super(c);
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+            if (changed || mBlurShader == null) {
+                LinearGradient gradient = new LinearGradient(
+                        0f,
+                        0f,
+                        right - left,
+                        bottom - top,
+                        Color.CYAN,
+                        Color.YELLOW,
+                        Shader.TileMode.CLAMP
+                );
+                mBlurShader = new BlurShader(30f, 40f, gradient);
+                mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+                mPaint.setShader(mBlurShader);
+            }
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+            canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
+        }
+    }
+
+    public static class BlurView extends View {
+
+        private final BlurShader mBlurShader;
+        private final Paint mPaint;
+
+        public BlurView(Context c) {
+            super(c);
+
+            mBlurShader = new BlurShader(20f, 20f, null);
+            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            mPaint.setShader(mBlurShader);
+        }
+
+        @Override
+        protected void onDraw(Canvas canvas) {
+            super.onDraw(canvas);
+
+            mPaint.setColor(Color.BLUE);
+            canvas.drawRect(0, 0, getWidth(), getHeight(), mPaint);
+
+            mPaint.setColor(Color.RED);
+            canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, 50f, mPaint);
+        }
+    }
+}
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index 76f8df0..f55d4d4 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -26,7 +26,7 @@
 java_test_host {
     name: "StagedInstallInternalTest",
     srcs: ["src/**/*.java"],
-    libs: ["tradefed"],
+    libs: ["tradefed", "cts-shim-host-lib"],
     static_libs: [
         "testng",
         "compatibility-tradefed",
@@ -35,6 +35,7 @@
         "cts-install-lib-host",
     ],
     data: [
+        ":com.android.apex.apkrollback.test_v1",
         ":com.android.apex.cts.shim.v2_prebuilt",
         ":TestAppAv1",
     ],
diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
index 7817239..e5411de 100644
--- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java
@@ -47,11 +47,7 @@
 
 @RunWith(JUnit4.class)
 public class StagedInstallInternalTest {
-
-    private static final String TAG = StagedInstallInternalTest.class.getSimpleName();
     private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test";
-    private static final TestApp TEST_APEX_WITH_APK_V1 = new TestApp("TestApexWithApkV1",
-            APK_IN_APEX_TESTAPEX_NAME, 1, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v1.apex");
     private static final TestApp TEST_APEX_WITH_APK_V2 = new TestApp("TestApexWithApkV2",
             APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v2.apex");
 
diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
index d7b0796..702f871 100644
--- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
+++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java
@@ -16,6 +16,8 @@
 
 package com.android.tests.stagedinstallinternal.host;
 
+import static com.android.cts.shim.lib.ShimPackage.SHIM_APEX_PACKAGE_NAME;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertTrue;
@@ -82,7 +84,8 @@
             Log.e(TAG, e);
         }
         deleteFiles("/system/apex/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
-                "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex");
+                "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex",
+                "/data/apex/active/" + SHIM_APEX_PACKAGE_NAME + "*.apex");
     }
 
     @Before
@@ -151,43 +154,78 @@
         runPhase("testSystemServerRestartDoesNotAffectStagedSessions_Verify");
     }
 
+    // Test waiting time for staged session to be ready using adb staged install can be altered
     @Test
-    public void testAdbStagedInstallWaitForReadyFlagWorks() throws Exception {
+    public void testAdbStagdReadyTimeoutFlagWorks() throws Exception {
         assumeTrue("Device does not support updating APEX",
                 mHostUtils.isApexUpdateSupported());
 
-        File apexFile = mTestUtils.getTestFile(SHIM_V2);
-        String output = getDevice().executeAdbCommand("install", "--staged",
-                "--wait-for-staged-ready", "60000", apexFile.getAbsolutePath());
+        final File apexFile = mTestUtils.getTestFile(SHIM_V2);
+        final String output = getDevice().executeAdbCommand("install", "--staged",
+                "--staged-ready-timeout", "60000", apexFile.getAbsolutePath());
         assertThat(output).contains("Reboot device to apply staged session");
-        String sessionId = getDevice().executeShellCommand(
+        final String sessionId = getDevice().executeShellCommand(
                 "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim();
         assertThat(sessionId).isNotEmpty();
     }
 
+    // Test adb staged installation wait for session to be ready by default
     @Test
-    public void testAdbStagedInstallNoWaitFlagWorks() throws Exception {
+    public void testAdbStagedInstallWaitsTillReadyByDefault() throws Exception {
         assumeTrue("Device does not support updating APEX",
                 mHostUtils.isApexUpdateSupported());
 
-        File apexFile = mTestUtils.getTestFile(SHIM_V2);
-        String output = getDevice().executeAdbCommand("install", "--staged",
-                "--no-wait", apexFile.getAbsolutePath());
+        final File apexFile = mTestUtils.getTestFile(SHIM_V2);
+        final String output = getDevice().executeAdbCommand("install", "--staged",
+                apexFile.getAbsolutePath());
+        assertThat(output).contains("Reboot device to apply staged session");
+        final String sessionId = getDevice().executeShellCommand(
+                "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim();
+        assertThat(sessionId).isNotEmpty();
+    }
+
+    // Test we can skip waiting for staged session to be ready
+    @Test
+    public void testAdbStagedReadyWaitCanBeSkipped() throws Exception {
+        assumeTrue("Device does not support updating APEX",
+                mHostUtils.isApexUpdateSupported());
+
+        final File apexFile = mTestUtils.getTestFile(SHIM_V2);
+        final String output = getDevice().executeAdbCommand("install", "--staged",
+                "--staged-ready-timeout", "0", apexFile.getAbsolutePath());
         assertThat(output).doesNotContain("Reboot device to apply staged session");
         assertThat(output).contains("Success");
-        String sessionId = getDevice().executeShellCommand(
+        final String sessionId = getDevice().executeShellCommand(
                 "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim();
         assertThat(sessionId).isEmpty();
     }
 
+    // Test rollback-app command waits for staged sessions to be ready
+    @Test
+    public void testAdbRollbackAppWaitsForStagedReady() throws Exception {
+        assumeTrue("Device does not support updating APEX",
+                mHostUtils.isApexUpdateSupported());
+
+        final File apexFile = mTestUtils.getTestFile(SHIM_V2);
+        String output = getDevice().executeAdbCommand("install", "--staged",
+                "--enable-rollback", apexFile.getAbsolutePath());
+        assertThat(output).contains("Reboot device to apply staged session");
+        getDevice().reboot();
+        output = getDevice().executeShellCommand("pm rollback-app " + SHIM_APEX_PACKAGE_NAME);
+        assertThat(output).contains("Reboot device to apply staged session");
+        final String sessionId = getDevice().executeShellCommand(
+                "pm get-stagedsessions --only-ready --only-parent --only-sessionid").trim();
+        assertThat(sessionId).isNotEmpty();
+    }
+
     @Test
     public void testAdbInstallMultiPackageCommandWorks() throws Exception {
         assumeTrue("Device does not support updating APEX",
                 mHostUtils.isApexUpdateSupported());
 
-        File apexFile = mTestUtils.getTestFile(SHIM_V2);
-        File apkFile = mTestUtils.getTestFile(APK_A);
-        String output = getDevice().executeAdbCommand("install-multi-package",
+        final File apexFile = mTestUtils.getTestFile(SHIM_V2);
+        final File apkFile = mTestUtils.getTestFile(APK_A);
+        final String output = getDevice().executeAdbCommand("install-multi-package",
                 apexFile.getAbsolutePath(), apkFile.getAbsolutePath());
         assertThat(output).contains("Created parent session");
         assertThat(output).contains("Created child session");
@@ -227,16 +265,16 @@
 
     private void restartSystemServer() throws Exception {
         // Restart the system server
-        ProcessInfo oldPs = getDevice().getProcessByName("system_server");
+        final ProcessInfo oldPs = getDevice().getProcessByName("system_server");
 
         getDevice().enableAdbRoot(); // Need root to restart system server
         assertThat(getDevice().executeShellCommand("am restart")).contains("Restart the system");
         getDevice().disableAdbRoot();
 
         // Wait for new system server process to start
-        long start = System.currentTimeMillis();
+        final long start = System.currentTimeMillis();
         while (System.currentTimeMillis() < start + SYSTEM_SERVER_TIMEOUT_MS) {
-            ProcessInfo newPs = getDevice().getProcessByName("system_server");
+            final ProcessInfo newPs = getDevice().getProcessByName("system_server");
             if (newPs != null) {
                 if (newPs.getPid() != oldPs.getPid()) {
                     getDevice().waitForDeviceAvailable();
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
index 073ae30..ca723b8 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
@@ -157,7 +157,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        mOrganizer.registerOrganizer(WINDOWING_MODE_MULTI_WINDOW);
+        mOrganizer.registerOrganizer();
 
         mTaskView1 = new ResizingTaskView(this, makeSettingsIntent());
         mTaskView2 = new ResizingTaskView(this, makeContactsIntent());
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
index 8fc5c5d..5ec9493 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
@@ -57,7 +57,7 @@
     public void onCreate() {
         super.onCreate();
 
-        mOrganizer.registerOrganizer(WINDOWING_MODE_PINNED);
+        mOrganizer.registerOrganizer();
 
         final WindowManager.LayoutParams wlp = new WindowManager.LayoutParams();
         wlp.setTitle("TaskOrganizerPipTest");
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index a3673df..bcb5c99 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1199,7 +1199,7 @@
         MockitoAnnotations.initMocks(this);
         when(mMetricsService.defaultNetworkMetrics()).thenReturn(mDefaultNetworkMetrics);
 
-        when(mUserManager.getUsers(eq(true))).thenReturn(
+        when(mUserManager.getAliveUsers()).thenReturn(
                 Arrays.asList(new UserInfo[] {
                         new UserInfo(VPN_USER, "", 0),
                 }));
diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
index ab12ac0..de35f91 100644
--- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -26,19 +26,14 @@
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_OEM;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRODUCT;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_VENDOR;
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
+import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_REQUIRED;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.content.pm.PackageManager.MATCH_ANY_USER;
-import static android.net.INetd.PERMISSION_INTERNET;
-import static android.net.INetd.PERMISSION_NONE;
-import static android.net.INetd.PERMISSION_SYSTEM;
-import static android.net.INetd.PERMISSION_UNINSTALLED;
-import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS;
-import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
 import static android.os.Process.SYSTEM_UID;
 
 import static com.android.server.connectivity.PermissionMonitor.NETWORK;
 import static com.android.server.connectivity.PermissionMonitor.SYSTEM;
-import static com.android.server.connectivity.PermissionMonitor.UidNetdPermissionInfo;
 
 import static junit.framework.Assert.fail;
 
@@ -69,7 +64,7 @@
 import android.os.Build;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.util.SparseArray;
+import android.util.SparseIntArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
@@ -102,6 +97,7 @@
     private static final int SYSTEM_UID1 = 1000;
     private static final int SYSTEM_UID2 = 1008;
     private static final int VPN_UID = 10002;
+    private static final String REAL_SYSTEM_PACKAGE_NAME = "android";
     private static final String MOCK_PACKAGE1 = "appName1";
     private static final String MOCK_PACKAGE2 = "appName2";
     private static final String SYSTEM_PACKAGE1 = "sysName1";
@@ -127,12 +123,11 @@
         MockitoAnnotations.initMocks(this);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
         when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
-        when(mUserManager.getUsers(eq(true))).thenReturn(
+        when(mUserManager.getAliveUsers()).thenReturn(
                 Arrays.asList(new UserInfo[] {
                         new UserInfo(MOCK_USER1, "", 0),
                         new UserInfo(MOCK_USER2, "", 0),
                 }));
-        doReturn(PackageManager.PERMISSION_DENIED).when(mDeps).uidPermission(anyString(), anyInt());
 
         mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
 
@@ -145,15 +140,35 @@
         verify(mMockPmi).getPackageList(mPermissionMonitor);
     }
 
-    private boolean wouldBeCarryoverPackage(String partition, int targetSdkVersion, int uid) {
-        final PackageInfo packageInfo = buildPackageInfo(partition, uid, MOCK_USER1);
+    private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid,
+            String... permissions) {
+        final PackageInfo packageInfo =
+                packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, permissions, partition);
         packageInfo.applicationInfo.targetSdkVersion = targetSdkVersion;
-        return mPermissionMonitor.isCarryoverPackage(packageInfo.applicationInfo);
+        packageInfo.applicationInfo.uid = uid;
+        return mPermissionMonitor.hasRestrictedNetworkPermission(packageInfo);
     }
 
-    private static PackageInfo packageInfoWithPartition(String partition) {
+    private static PackageInfo systemPackageInfoWithPermissions(String... permissions) {
+        return packageInfoWithPermissions(
+                REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM);
+    }
+
+    private static PackageInfo vendorPackageInfoWithPermissions(String... permissions) {
+        return packageInfoWithPermissions(
+                REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_VENDOR);
+    }
+
+    private static PackageInfo packageInfoWithPermissions(int permissionsFlags,
+            String[] permissions, String partition) {
+        int[] requestedPermissionsFlags = new int[permissions.length];
+        for (int i = 0; i < permissions.length; i++) {
+            requestedPermissionsFlags[i] = permissionsFlags;
+        }
         final PackageInfo packageInfo = new PackageInfo();
+        packageInfo.requestedPermissions = permissions;
         packageInfo.applicationInfo = new ApplicationInfo();
+        packageInfo.requestedPermissionsFlags = requestedPermissionsFlags;
         int privateFlags = 0;
         switch (partition) {
             case PARTITION_OEM:
@@ -170,145 +185,168 @@
         return packageInfo;
     }
 
-    private static PackageInfo buildPackageInfo(String partition, int uid, int userId) {
-        final PackageInfo pkgInfo = packageInfoWithPartition(partition);
+    private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid, int userId) {
+        final PackageInfo pkgInfo;
+        if (hasSystemPermission) {
+            pkgInfo = systemPackageInfoWithPermissions(
+                    CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+        } else {
+            pkgInfo = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED, new String[] {}, "");
+        }
         pkgInfo.applicationInfo.uid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
         return pkgInfo;
     }
 
-    /** This will REMOVE all previously set permissions from given uid. */
-    private void removeAllPermissions(int uid) {
-        doReturn(PackageManager.PERMISSION_DENIED).when(mDeps).uidPermission(anyString(), eq(uid));
-    }
-
-    /** Set up mocks so that given UID has the requested permissions. */
-    private void addPermissions(int uid, String... permissions) {
-        for (String permission : permissions) {
-            doReturn(PackageManager.PERMISSION_GRANTED)
-                    .when(mDeps).uidPermission(eq(permission), eq(uid));
-        }
-    }
-
     @Test
     public void testHasPermission() {
-        addPermissions(MOCK_UID1);
-        assertFalse(mPermissionMonitor.hasPermission(CHANGE_NETWORK_STATE, MOCK_UID1));
-        assertFalse(mPermissionMonitor.hasPermission(NETWORK_STACK, MOCK_UID1));
-        assertFalse(mPermissionMonitor.hasPermission(
-                CONNECTIVITY_USE_RESTRICTED_NETWORKS, MOCK_UID1));
-        assertFalse(mPermissionMonitor.hasPermission(CONNECTIVITY_INTERNAL, MOCK_UID1));
+        PackageInfo app = systemPackageInfoWithPermissions();
+        assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+        assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+        assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
 
-        addPermissions(MOCK_UID1, CHANGE_NETWORK_STATE, NETWORK_STACK);
-        assertTrue(mPermissionMonitor.hasPermission(CHANGE_NETWORK_STATE, MOCK_UID1));
-        assertTrue(mPermissionMonitor.hasPermission(NETWORK_STACK, MOCK_UID1));
-        assertFalse(mPermissionMonitor.hasPermission(
-                CONNECTIVITY_USE_RESTRICTED_NETWORKS, MOCK_UID1));
-        assertFalse(mPermissionMonitor.hasPermission(CONNECTIVITY_INTERNAL, MOCK_UID1));
-        assertFalse(mPermissionMonitor.hasPermission(CHANGE_NETWORK_STATE, MOCK_UID2));
-        assertFalse(mPermissionMonitor.hasPermission(NETWORK_STACK, MOCK_UID2));
+        app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE, NETWORK_STACK);
+        assertTrue(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+        assertTrue(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+        assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
 
-        addPermissions(MOCK_UID2, CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL);
-        assertFalse(mPermissionMonitor.hasPermission(
-                CONNECTIVITY_USE_RESTRICTED_NETWORKS, MOCK_UID1));
-        assertFalse(mPermissionMonitor.hasPermission(CONNECTIVITY_INTERNAL, MOCK_UID1));
-        assertTrue(mPermissionMonitor.hasPermission(
-                CONNECTIVITY_USE_RESTRICTED_NETWORKS, MOCK_UID2));
-        assertTrue(mPermissionMonitor.hasPermission(CONNECTIVITY_INTERNAL, MOCK_UID2));
+        app = systemPackageInfoWithPermissions(
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL);
+        assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+        assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+        assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertTrue(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+
+        app = packageInfoWithPermissions(REQUESTED_PERMISSION_REQUIRED, new String[] {
+                CONNECTIVITY_USE_RESTRICTED_NETWORKS, CONNECTIVITY_INTERNAL, NETWORK_STACK },
+                PARTITION_SYSTEM);
+        assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+        assertFalse(mPermissionMonitor.hasPermission(app, NETWORK_STACK));
+        assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertFalse(mPermissionMonitor.hasPermission(app, CONNECTIVITY_INTERNAL));
+
+        app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE);
+        app.requestedPermissions = null;
+        assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
+
+        app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE);
+        app.requestedPermissionsFlags = null;
+        assertFalse(mPermissionMonitor.hasPermission(app, CHANGE_NETWORK_STATE));
     }
 
     @Test
     public void testIsVendorApp() {
-        PackageInfo app = packageInfoWithPartition(PARTITION_SYSTEM);
+        PackageInfo app = systemPackageInfoWithPermissions();
         assertFalse(mPermissionMonitor.isVendorApp(app.applicationInfo));
-        app = packageInfoWithPartition(PARTITION_OEM);
+        app = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED,
+                new String[] {}, PARTITION_OEM);
         assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo));
-        app = packageInfoWithPartition(PARTITION_PRODUCT);
+        app = packageInfoWithPermissions(REQUESTED_PERMISSION_GRANTED,
+                new String[] {}, PARTITION_PRODUCT);
         assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo));
-        app = packageInfoWithPartition(PARTITION_VENDOR);
+        app = vendorPackageInfoWithPermissions();
         assertTrue(mPermissionMonitor.isVendorApp(app.applicationInfo));
     }
 
-    /**
-     * Remove all permissions from the uid then setup permissions to uid for checking restricted
-     * network permission.
-     */
-    private void assertRestrictedNetworkPermission(boolean hasPermission, int uid,
-            String... permissions) {
-        removeAllPermissions(uid);
-        addPermissions(uid, permissions);
-        assertEquals(hasPermission, mPermissionMonitor.hasRestrictedNetworkPermission(uid));
+    @Test
+    public void testHasNetworkPermission() {
+        PackageInfo app = systemPackageInfoWithPermissions();
+        assertFalse(mPermissionMonitor.hasNetworkPermission(app));
+        app = systemPackageInfoWithPermissions(CHANGE_NETWORK_STATE);
+        assertTrue(mPermissionMonitor.hasNetworkPermission(app));
+        app = systemPackageInfoWithPermissions(NETWORK_STACK);
+        assertFalse(mPermissionMonitor.hasNetworkPermission(app));
+        app = systemPackageInfoWithPermissions(CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+        assertFalse(mPermissionMonitor.hasNetworkPermission(app));
+        app = systemPackageInfoWithPermissions(CONNECTIVITY_INTERNAL);
+        assertFalse(mPermissionMonitor.hasNetworkPermission(app));
     }
 
     @Test
     public void testHasRestrictedNetworkPermission() {
-        assertRestrictedNetworkPermission(false, MOCK_UID1);
-        assertRestrictedNetworkPermission(false, MOCK_UID1, CHANGE_NETWORK_STATE);
-        assertRestrictedNetworkPermission(true, MOCK_UID1, NETWORK_STACK);
-        assertRestrictedNetworkPermission(false, MOCK_UID1, CONNECTIVITY_INTERNAL);
-        assertRestrictedNetworkPermission(true, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
-        assertRestrictedNetworkPermission(false, MOCK_UID1, CHANGE_WIFI_STATE);
-        assertRestrictedNetworkPermission(true, MOCK_UID1, PERMISSION_MAINLINE_NETWORK_STACK);
+        assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
+        assertFalse(hasRestrictedNetworkPermission(
+                PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+        assertTrue(hasRestrictedNetworkPermission(
+                PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK));
+        assertFalse(hasRestrictedNetworkPermission(
+                PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+        assertTrue(hasRestrictedNetworkPermission(
+                PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertFalse(hasRestrictedNetworkPermission(
+                PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
 
-        assertFalse(mPermissionMonitor.hasRestrictedNetworkPermission(MOCK_UID2));
-        assertFalse(mPermissionMonitor.hasRestrictedNetworkPermission(SYSTEM_UID));
+        assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
+        assertFalse(hasRestrictedNetworkPermission(
+                PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL));
     }
 
     @Test
-    public void testIsCarryoverPackage() {
+    public void testHasRestrictedNetworkPermissionSystemUid() {
         doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt();
-        assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
-        assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
-        assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
-        assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
-        assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+        assertTrue(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
+        assertTrue(hasRestrictedNetworkPermission(
+                PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_INTERNAL));
+        assertTrue(hasRestrictedNetworkPermission(
+                PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
 
         doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
-        assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
-        assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
-        assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+        assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
+        assertFalse(hasRestrictedNetworkPermission(
+                PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL));
+        assertTrue(hasRestrictedNetworkPermission(
+                PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+    }
 
-        assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, SYSTEM_UID));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, SYSTEM_UID));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, MOCK_UID1));
-        assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, MOCK_UID1));
+    @Test
+    public void testHasRestrictedNetworkPermissionVendorApp() {
+        assertTrue(hasRestrictedNetworkPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+        assertTrue(hasRestrictedNetworkPermission(
+                PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+        assertTrue(hasRestrictedNetworkPermission(
+                PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK));
+        assertTrue(hasRestrictedNetworkPermission(
+                PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+        assertTrue(hasRestrictedNetworkPermission(
+                PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+        assertTrue(hasRestrictedNetworkPermission(
+                PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
+
+        assertFalse(hasRestrictedNetworkPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+        assertFalse(hasRestrictedNetworkPermission(
+                PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL));
+        assertFalse(hasRestrictedNetworkPermission(
+                PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_NETWORK_STATE));
     }
 
     private void assertBackgroundPermission(boolean hasPermission, String name, int uid,
             String... permissions) throws Exception {
         when(mPackageManager.getPackageInfo(eq(name), anyInt()))
-                .thenReturn(buildPackageInfo(PARTITION_SYSTEM, uid, MOCK_USER1));
-        addPermissions(uid, permissions);
+                .thenReturn(packageInfoWithPermissions(
+                        REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM));
         mPermissionMonitor.onPackageAdded(name, uid);
         assertEquals(hasPermission, mPermissionMonitor.hasUseBackgroundNetworksPermission(uid));
     }
 
     @Test
     public void testHasUseBackgroundNetworksPermission() throws Exception {
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID1));
-        assertBackgroundPermission(false, "mock1", MOCK_UID1);
-        assertBackgroundPermission(false, "mock2", MOCK_UID1, CONNECTIVITY_INTERNAL);
-        assertBackgroundPermission(true, "mock3", MOCK_UID1, NETWORK_STACK);
+        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(SYSTEM_UID));
+        assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID);
+        assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID, CONNECTIVITY_INTERNAL);
+        assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, CHANGE_NETWORK_STATE);
+        assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, NETWORK_STACK);
 
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID2));
-        assertBackgroundPermission(false, "mock4", MOCK_UID2);
-        assertBackgroundPermission(true, "mock5", MOCK_UID2,
+        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID1));
+        assertBackgroundPermission(false, MOCK_PACKAGE1, MOCK_UID1);
+        assertBackgroundPermission(true, MOCK_PACKAGE1, MOCK_UID1,
                 CONNECTIVITY_USE_RESTRICTED_NETWORKS);
 
-        doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
-        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(SYSTEM_UID));
-        assertBackgroundPermission(false, "system1", SYSTEM_UID);
-        assertBackgroundPermission(true, "system2", SYSTEM_UID, CHANGE_NETWORK_STATE);
-        doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt();
-        removeAllPermissions(SYSTEM_UID);
-        assertBackgroundPermission(true, "system3", SYSTEM_UID);
+        assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID2));
+        assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2);
+        assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2,
+                CONNECTIVITY_INTERNAL);
+        assertBackgroundPermission(true, MOCK_PACKAGE2, MOCK_UID2, NETWORK_STACK);
     }
 
     private class NetdMonitor {
@@ -318,7 +356,7 @@
             // Add hook to verify and track result of setPermission.
             doAnswer((InvocationOnMock invocation) -> {
                 final Object[] args = invocation.getArguments();
-                final Boolean isSystem = args[0].equals(PERMISSION_SYSTEM);
+                final Boolean isSystem = args[0].equals(INetd.PERMISSION_SYSTEM);
                 for (final int uid : (int[]) args[1]) {
                     // TODO: Currently, permission monitor will send duplicate commands for each uid
                     // corresponding to each user. Need to fix that and uncomment below test.
@@ -378,14 +416,13 @@
         // MOCK_UID1: MOCK_PACKAGE1 only has network permission.
         // SYSTEM_UID: SYSTEM_PACKAGE1 has system permission.
         // SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission.
-        doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM),
-                anyString(), anyInt());
+        doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString());
         doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(),
-                eq(SYSTEM_PACKAGE1), anyInt());
+                eq(SYSTEM_PACKAGE1));
         doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
-                eq(SYSTEM_PACKAGE2), anyInt());
+                eq(SYSTEM_PACKAGE2));
         doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
-                eq(MOCK_PACKAGE1), anyInt());
+                eq(MOCK_PACKAGE1));
 
         // Add SYSTEM_PACKAGE2, expect only have network permission.
         mPermissionMonitor.onUserAdded(MOCK_USER1);
@@ -436,15 +473,13 @@
     public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception {
         when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
                 Arrays.asList(new PackageInfo[] {
-                        buildPackageInfo(PARTITION_SYSTEM, SYSTEM_UID1, MOCK_USER1),
-                        buildPackageInfo(PARTITION_SYSTEM, MOCK_UID1, MOCK_USER1),
-                        buildPackageInfo(PARTITION_SYSTEM, MOCK_UID2, MOCK_USER1),
-                        buildPackageInfo(PARTITION_SYSTEM, VPN_UID, MOCK_USER1)
+                        buildPackageInfo(/* SYSTEM */ true, SYSTEM_UID1, MOCK_USER1),
+                        buildPackageInfo(/* SYSTEM */ false, MOCK_UID1, MOCK_USER1),
+                        buildPackageInfo(/* SYSTEM */ false, MOCK_UID2, MOCK_USER1),
+                        buildPackageInfo(/* SYSTEM */ false, VPN_UID, MOCK_USER1)
                 }));
         when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
-                buildPackageInfo(PARTITION_SYSTEM, MOCK_UID1, MOCK_USER1));
-        addPermissions(SYSTEM_UID,
-                CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+                buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
         mPermissionMonitor.startMonitoring();
         // Every app on user 0 except MOCK_UID2 are under VPN.
         final Set<UidRange> vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] {
@@ -489,11 +524,11 @@
     public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception {
         when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
                 Arrays.asList(new PackageInfo[] {
-                        buildPackageInfo(PARTITION_SYSTEM, SYSTEM_UID1, MOCK_USER1),
-                        buildPackageInfo(PARTITION_SYSTEM, VPN_UID, MOCK_USER1)
+                        buildPackageInfo(true, SYSTEM_UID1, MOCK_USER1),
+                        buildPackageInfo(false, VPN_UID, MOCK_USER1)
                 }));
         when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE1), eq(GET_PERMISSIONS))).thenReturn(
-                        buildPackageInfo(PARTITION_SYSTEM, MOCK_UID1, MOCK_USER1));
+                        buildPackageInfo(false, MOCK_UID1, MOCK_USER1));
 
         mPermissionMonitor.startMonitoring();
         final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1));
@@ -561,48 +596,47 @@
         // SYSTEM_UID1: SYSTEM_PACKAGE1 has internet permission and update device stats permission.
         // SYSTEM_UID2: SYSTEM_PACKAGE2 has only update device stats permission.
 
-        final SparseArray<UidNetdPermissionInfo> uidsPermInfo = new SparseArray<>();
-        uidsPermInfo.put(MOCK_UID1, new UidNetdPermissionInfo(PERMISSION_INTERNET));
-        uidsPermInfo.put(MOCK_UID2, new UidNetdPermissionInfo(PERMISSION_NONE));
-        uidsPermInfo.put(SYSTEM_UID1, new UidNetdPermissionInfo(
-                PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS));
-        uidsPermInfo.put(SYSTEM_UID2, new UidNetdPermissionInfo(PERMISSION_UPDATE_DEVICE_STATS));
+        SparseIntArray netdPermissionsAppIds = new SparseIntArray();
+        netdPermissionsAppIds.put(MOCK_UID1, INetd.PERMISSION_INTERNET);
+        netdPermissionsAppIds.put(MOCK_UID2, INetd.PERMISSION_NONE);
+        netdPermissionsAppIds.put(SYSTEM_UID1, INetd.PERMISSION_INTERNET
+                | INetd.PERMISSION_UPDATE_DEVICE_STATS);
+        netdPermissionsAppIds.put(SYSTEM_UID2, INetd.PERMISSION_UPDATE_DEVICE_STATS);
 
         // Send the permission information to netd, expect permission updated.
-        mPermissionMonitor.sendPackagePermissionsToNetd(uidsPermInfo);
+        mPermissionMonitor.sendPackagePermissionsToNetd(netdPermissionsAppIds);
 
-        mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET,
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET,
                 new int[]{MOCK_UID1});
-        mNetdServiceMonitor.expectPermission(PERMISSION_NONE, new int[]{MOCK_UID2});
-        mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
-                | PERMISSION_UPDATE_DEVICE_STATS, new int[]{SYSTEM_UID1});
-        mNetdServiceMonitor.expectPermission(PERMISSION_UPDATE_DEVICE_STATS,
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID2});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+                | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{SYSTEM_UID1});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UPDATE_DEVICE_STATS,
                 new int[]{SYSTEM_UID2});
 
         // Update permission of MOCK_UID1, expect new permission show up.
-        mPermissionMonitor.sendPackagePermissionsForUid(MOCK_UID1, new UidNetdPermissionInfo(
-                PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS));
-        mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
-                | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+        mPermissionMonitor.sendPackagePermissionsForUid(MOCK_UID1,
+                INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS);
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+                | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
 
         // Change permissions of SYSTEM_UID2, expect new permission show up and old permission
         // revoked.
-        mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID2, new UidNetdPermissionInfo(
-                PERMISSION_INTERNET));
-        mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{SYSTEM_UID2});
+        mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID2,
+                INetd.PERMISSION_INTERNET);
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{SYSTEM_UID2});
 
         // Revoke permission from SYSTEM_UID1, expect no permission stored.
-        mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID1, new UidNetdPermissionInfo(
-                PERMISSION_NONE));
-        mNetdServiceMonitor.expectPermission(PERMISSION_NONE, new int[]{SYSTEM_UID1});
+        mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID1, INetd.PERMISSION_NONE);
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{SYSTEM_UID1});
     }
 
     private PackageInfo setPackagePermissions(String packageName, int uid, String[] permissions)
             throws Exception {
-        final PackageInfo packageInfo = buildPackageInfo(PARTITION_SYSTEM, uid, MOCK_USER1);
+        PackageInfo packageInfo = packageInfoWithPermissions(
+                REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM);
         when(mPackageManager.getPackageInfo(eq(packageName), anyInt())).thenReturn(packageInfo);
         when(mPackageManager.getPackagesForUid(eq(uid))).thenReturn(new String[]{packageName});
-        addPermissions(uid, permissions);
         return packageInfo;
     }
 
@@ -618,30 +652,31 @@
         final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
 
         addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
-        mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
-                | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+                | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
 
         addPackage(MOCK_PACKAGE2, MOCK_UID2, new String[] {INTERNET});
-        mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{MOCK_UID2});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID2});
     }
 
     @Test
     public void testPackageInstallSharedUid() throws Exception {
         final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
 
-        addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
-        mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
-                | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+        PackageInfo packageInfo1 = addPackage(MOCK_PACKAGE1, MOCK_UID1,
+                new String[] {INTERNET, UPDATE_DEVICE_STATS});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+                | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
 
         // Install another package with the same uid and no permissions should not cause the UID to
         // lose permissions.
-        final PackageInfo packageInfo2 = buildPackageInfo(PARTITION_SYSTEM, MOCK_UID1, MOCK_USER1);
+        PackageInfo packageInfo2 = systemPackageInfoWithPermissions();
         when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2);
         when(mPackageManager.getPackagesForUid(MOCK_UID1))
               .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2});
         mPermissionMonitor.onPackageAdded(MOCK_PACKAGE2, MOCK_UID1);
-        mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
-                | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+                | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
     }
 
     @Test
@@ -649,12 +684,12 @@
         final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
 
         addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
-        mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
-                | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+                | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
 
         when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
         mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
-        mNetdServiceMonitor.expectPermission(PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
     }
 
     @Test
@@ -662,16 +697,15 @@
         final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
 
         addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
-        mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
-                | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+                | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
 
         when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
-        removeAllPermissions(MOCK_UID1);
         mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
-        mNetdServiceMonitor.expectPermission(PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_UNINSTALLED, new int[]{MOCK_UID1});
 
         addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET});
-        mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{MOCK_UID1});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1});
     }
 
     @Test
@@ -679,10 +713,10 @@
         final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
 
         addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {});
-        mNetdServiceMonitor.expectPermission(PERMISSION_NONE, new int[]{MOCK_UID1});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_NONE, new int[]{MOCK_UID1});
 
         addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET});
-        mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{MOCK_UID1});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1});
     }
 
     @Test
@@ -690,19 +724,17 @@
         final NetdServiceMonitor mNetdServiceMonitor = new NetdServiceMonitor(mNetdService);
 
         addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
-        mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET
-                | PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET
+                | INetd.PERMISSION_UPDATE_DEVICE_STATS, new int[]{MOCK_UID1});
 
         // Mock another package with the same uid but different permissions.
-        final PackageInfo packageInfo2 = buildPackageInfo(PARTITION_SYSTEM, MOCK_UID1, MOCK_USER1);
+        PackageInfo packageInfo2 = systemPackageInfoWithPermissions(INTERNET);
         when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2);
         when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{
                 MOCK_PACKAGE2});
-        removeAllPermissions(MOCK_UID1);
-        addPermissions(MOCK_UID1, INTERNET);
 
         mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
-        mNetdServiceMonitor.expectPermission(PERMISSION_INTERNET, new int[]{MOCK_UID1});
+        mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1});
     }
 
     @Test
@@ -711,6 +743,9 @@
         // necessary permission.
         final Context realContext = InstrumentationRegistry.getContext();
         final PermissionMonitor monitor = new PermissionMonitor(realContext, mNetdService);
-        assertTrue(monitor.hasPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, SYSTEM_UID));
+        final PackageManager manager = realContext.getPackageManager();
+        final PackageInfo systemInfo = manager.getPackageInfo(REAL_SYSTEM_PACKAGE_NAME,
+                GET_PERMISSIONS | MATCH_ANY_USER);
+        assertTrue(monitor.hasPermission(systemInfo, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
     }
 }
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index e8c4ee9..c76b4cd 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -1238,15 +1238,14 @@
          * @see UserManagerService#getUsers(boolean)
          */
         doAnswer(invocation -> {
-            final boolean excludeDying = (boolean) invocation.getArguments()[0];
             final ArrayList<UserInfo> result = new ArrayList<>(users.length);
             for (UserInfo ui : users) {
-                if (!excludeDying || (ui.isEnabled() && !ui.partial)) {
+                if (ui.isEnabled() && !ui.partial) {
                     result.add(ui);
                 }
             }
             return result;
-        }).when(mUserManager).getUsers(anyBoolean());
+        }).when(mUserManager).getAliveUsers();
 
         doAnswer(invocation -> {
             final int id = (int) invocation.getArguments()[0];
diff --git a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
index 6dc4fce..8f09377 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsSubscriptionsMonitorTest.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.when;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.NetworkTemplate;
 import android.os.test.TestLooper;
@@ -135,6 +136,11 @@
         mMonitor.onSubscriptionsChanged();
     }
 
+    private void updateSubscriberIdForTestSub(int subId, @Nullable final String subscriberId) {
+        when(mTelephonyManager.getSubscriberId(subId)).thenReturn(subscriberId);
+        mMonitor.onSubscriptionsChanged();
+    }
+
     private void removeTestSub(int subId) {
         // Remove subId from TestSubList.
         mTestSubList.removeIf(it -> it == subId);
@@ -268,4 +274,54 @@
         listener.onServiceStateChanged(serviceState);
         assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_NR);
     }
+
+    @Test
+    public void testSubscriberIdUnavailable() {
+        final ArgumentCaptor<RatTypeListener> ratTypeListenerCaptor =
+                ArgumentCaptor.forClass(RatTypeListener.class);
+
+        mMonitor.start();
+        // Insert sim1, set subscriberId to null which is normal in SIM PIN locked case.
+        // Verify RAT type is NETWORK_TYPE_UNKNOWN and service will not perform listener
+        // registration.
+        addTestSub(TEST_SUBID1, null);
+        verify(mTelephonyManager, never()).listen(any(), anyInt());
+        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+
+        // Set IMSI for sim1, verify the listener will be registered.
+        updateSubscriberIdForTestSub(TEST_SUBID1, TEST_IMSI1);
+        verify(mTelephonyManager, times(1)).listen(ratTypeListenerCaptor.capture(),
+                eq(PhoneStateListener.LISTEN_SERVICE_STATE));
+        reset(mTelephonyManager);
+        when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+
+        // Set RAT type of sim1 to UMTS. Verify RAT type of sim1 is changed.
+        setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+                TelephonyManager.NETWORK_TYPE_UMTS);
+        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+        reset(mDelegate);
+
+        // Set IMSI to null again to simulate somehow IMSI is not available, such as
+        // modem crash. Verify service should not unregister listener.
+        updateSubscriberIdForTestSub(TEST_SUBID1, null);
+        verify(mTelephonyManager, never()).listen(any(), anyInt());
+        assertRatTypeNotChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UMTS);
+        reset(mDelegate);
+
+        // Set RAT type of sim1 to LTE. Verify RAT type of sim1 is still changed even if the IMSI
+        // is not available. The monitor keeps the listener even if the IMSI disappears because
+        // the IMSI can never change for any given subId, therefore even if the IMSI is updated
+        // to null, the monitor should continue accepting updates of the RAT type. However,
+        // telephony is never actually supposed to do this, if the IMSI disappears there should
+        // not be updates, but it's still the right thing to do theoretically.
+        setRatTypeForSub(ratTypeListenerCaptor.getAllValues(), TEST_SUBID1,
+                TelephonyManager.NETWORK_TYPE_LTE);
+        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_LTE);
+        reset(mDelegate);
+
+        mMonitor.stop();
+        verify(mTelephonyManager, times(1)).listen(eq(ratTypeListenerCaptor.getValue()),
+                eq(PhoneStateListener.LISTEN_NONE));
+        assertRatTypeChangedForSub(TEST_IMSI1, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+    }
 }
diff --git a/tools/preload-check/Android.bp b/tools/preload-check/Android.bp
index 87b31d2..aaa6d76 100644
--- a/tools/preload-check/Android.bp
+++ b/tools/preload-check/Android.bp
@@ -15,7 +15,7 @@
 java_test_host {
     name: "PreloadCheck",
     srcs: ["src/**/*.java"],
-    java_resources: [":preloaded-classes-blacklist"],
+    java_resources: [":preloaded-classes-denylist"],
     libs: ["tradefed"],
     test_suites: ["general-tests"],
     required: ["preload-check-device"],
diff --git a/tools/preload-check/src/com/android/preload/check/PreloadCheck.java b/tools/preload-check/src/com/android/preload/check/PreloadCheck.java
index 00fd414e3..3d85153 100644
--- a/tools/preload-check/src/com/android/preload/check/PreloadCheck.java
+++ b/tools/preload-check/src/com/android/preload/check/PreloadCheck.java
@@ -69,13 +69,13 @@
     }
 
     /**
-     * Test the classes mentioned in the embedded preloaded-classes blacklist.
+     * Test the classes mentioned in the embedded preloaded-classes denylist.
      */
     @Test
-    public void testBlackList() throws Exception {
+    public void testDenyList() throws Exception {
         StringBuilder sb = new StringBuilder();
         try (BufferedReader br = new BufferedReader(new InputStreamReader(getClass()
-                .getResourceAsStream("/preloaded-classes-blacklist")))) {
+                .getResourceAsStream("/preloaded-classes-denylist")))) {
             String s;
             while ((s = br.readLine()) != null) {
                 s = s.trim();
diff --git a/wifi/api/current.txt b/wifi/api/current.txt
index 53c3b33..ee7320f 100644
--- a/wifi/api/current.txt
+++ b/wifi/api/current.txt
@@ -487,6 +487,7 @@
     method @Nullable public String getPassphrase();
     method @Nullable public android.net.wifi.hotspot2.PasspointConfiguration getPasspointConfig();
     method @IntRange(from=0) public int getPriority();
+    method public int getPriorityGroup();
     method @Nullable public String getSsid();
     method public boolean isAppInteractionRequired();
     method public boolean isCredentialSharedWithUser();
@@ -513,6 +514,7 @@
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setIsUserInteractionRequired(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPasspointConfig(@NonNull android.net.wifi.hotspot2.PasspointConfiguration);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPriority(@IntRange(from=0) int);
+    method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setPriorityGroup(int);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setSsid(@NonNull String);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setUntrusted(boolean);
     method @NonNull public android.net.wifi.WifiNetworkSuggestion.Builder setWapiEnterpriseConfig(@NonNull android.net.wifi.WifiEnterpriseConfig);
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index 68eb1bb..a3c4ae7 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -102,10 +102,15 @@
          */
         private int mMeteredOverride;
         /**
-         * Priority of this network among other network suggestions provided by the app.
+         * Priority of this network among other network suggestions from same priority group
+         * provided by the app.
          * The lower the number, the higher the priority (i.e value of 0 = highest priority).
          */
         private int mPriority;
+        /**
+         * Priority group ID, while suggestion priority will only effect inside the priority group.
+         */
+        private int mPriorityGroup;
 
         /**
          * The carrier ID identifies the operator who provides this network configuration.
@@ -165,6 +170,7 @@
             mWapiPskPassphrase = null;
             mWapiEnterpriseConfig = null;
             mIsNetworkUntrusted = false;
+            mPriorityGroup = 0;
         }
 
         /**
@@ -329,6 +335,18 @@
         }
 
         /**
+         * Set the priority group ID, {@link #setPriority(int)} will only impact the network
+         * suggestions from the same priority group within the same app.
+         *
+         * @param priorityGroup priority group id, if not set default is 0.
+         * @return Instance of {@link Builder} to enable chaining of the builder method.
+         */
+        public @NonNull Builder setPriorityGroup(int priorityGroup) {
+            mPriorityGroup = priorityGroup;
+            return this;
+        }
+
+        /**
          * Set the ASCII WAPI passphrase for this network. Needed for authenticating to
          * WAPI-PSK networks.
          *
@@ -411,8 +429,9 @@
 
         /**
          * Specify the priority of this network among other network suggestions provided by the same
-         * app (priorities have no impact on suggestions by different apps). The higher the number,
-         * the higher the priority (i.e value of 0 = lowest priority).
+         * app (priorities have no impact on suggestions by different apps) and within the same
+         * priority group, see {@link #setPriorityGroup(int)}.
+         * The higher the number, the higher the priority (i.e value of 0 = lowest priority).
          * <p>
          * <li>If not set, defaults a lower priority than any assigned priority.</li>
          *
@@ -696,7 +715,8 @@
                     mIsAppInteractionRequired,
                     mIsUserInteractionRequired,
                     mIsSharedWithUser,
-                    mIsInitialAutojoinEnabled);
+                    mIsInitialAutojoinEnabled,
+                    mPriorityGroup);
         }
     }
 
@@ -739,6 +759,12 @@
      */
     public final boolean isInitialAutoJoinEnabled;
 
+    /**
+     * Priority group ID.
+     * @hide
+     */
+    public final int priorityGroup;
+
     /** @hide */
     public WifiNetworkSuggestion() {
         this.wifiConfiguration = new WifiConfiguration();
@@ -747,6 +773,7 @@
         this.isUserInteractionRequired = false;
         this.isUserAllowedToManuallyConnect = true;
         this.isInitialAutoJoinEnabled = true;
+        this.priorityGroup = 0;
     }
 
     /** @hide */
@@ -755,7 +782,7 @@
                                  boolean isAppInteractionRequired,
                                  boolean isUserInteractionRequired,
                                  boolean isUserAllowedToManuallyConnect,
-                                 boolean isInitialAutoJoinEnabled) {
+                                 boolean isInitialAutoJoinEnabled, int priorityGroup) {
         checkNotNull(networkConfiguration);
         this.wifiConfiguration = networkConfiguration;
         this.passpointConfiguration = passpointConfiguration;
@@ -764,6 +791,7 @@
         this.isUserInteractionRequired = isUserInteractionRequired;
         this.isUserAllowedToManuallyConnect = isUserAllowedToManuallyConnect;
         this.isInitialAutoJoinEnabled = isInitialAutoJoinEnabled;
+        this.priorityGroup = priorityGroup;
     }
 
     public static final @NonNull Creator<WifiNetworkSuggestion> CREATOR =
@@ -776,7 +804,8 @@
                             in.readBoolean(), // isAppInteractionRequired
                             in.readBoolean(), // isUserInteractionRequired
                             in.readBoolean(), // isSharedCredentialWithUser
-                            in.readBoolean()  // isAutojoinEnabled
+                            in.readBoolean(),  // isAutojoinEnabled
+                            in.readInt() // priorityGroup
                     );
                 }
 
@@ -799,6 +828,7 @@
         dest.writeBoolean(isUserInteractionRequired);
         dest.writeBoolean(isUserAllowedToManuallyConnect);
         dest.writeBoolean(isInitialAutoJoinEnabled);
+        dest.writeInt(priorityGroup);
     }
 
     @Override
@@ -842,6 +872,7 @@
                 .append(", isCredentialSharedWithUser=").append(isUserAllowedToManuallyConnect)
                 .append(", isInitialAutoJoinEnabled=").append(isInitialAutoJoinEnabled)
                 .append(", isUnTrusted=").append(!wifiConfiguration.trusted)
+                .append(", priorityGroup=").append(priorityGroup)
                 .append(" ]");
         return sb.toString();
     }
@@ -962,4 +993,11 @@
         }
         return WifiInfo.removeDoubleQuotes(wifiConfiguration.preSharedKey);
     }
+
+    /**
+     * @see Builder#setPriorityGroup(int)
+     */
+    public int getPriorityGroup() {
+        return priorityGroup;
+    }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index f0839e9..3744a51 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -16,7 +16,12 @@
 
 package android.net.wifi;
 
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 import android.net.MacAddress;
 import android.net.wifi.hotspot2.PasspointConfiguration;
@@ -39,6 +44,8 @@
     private static final String TEST_FQDN = "fqdn";
     private static final String TEST_WAPI_CERT_SUITE = "suite";
     private static final String TEST_DOMAIN_SUFFIX_MATCH = "domainSuffixMatch";
+    private static final int DEFAULT_PRIORITY_GROUP = 0;
+    private static final int TEST_PRIORITY_GROUP = 1;
 
     /**
      * Validate correctness of WifiNetworkSuggestion object created by
@@ -612,7 +619,7 @@
         configuration.BSSID = TEST_BSSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion(
-                configuration, null, false, true, true, true);
+                configuration, null, false, true, true, true, TEST_PRIORITY_GROUP);
 
         Parcel parcelW = Parcel.obtain();
         suggestion.writeToParcel(parcelW, 0);
@@ -683,14 +690,16 @@
         configuration.BSSID = TEST_BSSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
         WifiNetworkSuggestion suggestion =
-                new WifiNetworkSuggestion(configuration, null, true, false, true, true);
+                new WifiNetworkSuggestion(configuration, null, true, false, true, true,
+                        TEST_PRIORITY_GROUP);
 
         WifiConfiguration configuration1 = new WifiConfiguration();
         configuration1.SSID = TEST_SSID;
         configuration1.BSSID = TEST_BSSID;
         configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
         WifiNetworkSuggestion suggestion1 =
-                new WifiNetworkSuggestion(configuration1, null, false, true, true, true);
+                new WifiNetworkSuggestion(configuration1, null, false, true, true, true,
+                        DEFAULT_PRIORITY_GROUP);
 
         assertEquals(suggestion, suggestion1);
         assertEquals(suggestion.hashCode(), suggestion1.hashCode());
@@ -706,13 +715,15 @@
         configuration.SSID = TEST_SSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion =
-                new WifiNetworkSuggestion(configuration, null, false, false, true, true);
+                new WifiNetworkSuggestion(configuration, null, false, false, true, true,
+                        DEFAULT_PRIORITY_GROUP);
 
         WifiConfiguration configuration1 = new WifiConfiguration();
         configuration1.SSID = TEST_SSID_1;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion1 =
-                new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
+                new WifiNetworkSuggestion(configuration1, null, false, false, true, true,
+                        DEFAULT_PRIORITY_GROUP);
 
         assertNotEquals(suggestion, suggestion1);
     }
@@ -728,13 +739,15 @@
         configuration.BSSID = TEST_BSSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion =
-                new WifiNetworkSuggestion(configuration, null,  false, false, true, true);
+                new WifiNetworkSuggestion(configuration, null, false, false, true, true,
+                        DEFAULT_PRIORITY_GROUP);
 
         WifiConfiguration configuration1 = new WifiConfiguration();
         configuration1.SSID = TEST_SSID;
         configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion1 =
-                new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
+                new WifiNetworkSuggestion(configuration1, null, false, false, true, true,
+                        DEFAULT_PRIORITY_GROUP);
 
         assertNotEquals(suggestion, suggestion1);
     }
@@ -749,13 +762,15 @@
         configuration.SSID = TEST_SSID;
         configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
         WifiNetworkSuggestion suggestion =
-                new WifiNetworkSuggestion(configuration, null, false, false, true, true);
+                new WifiNetworkSuggestion(configuration, null, false, false, true, true,
+                        DEFAULT_PRIORITY_GROUP);
 
         WifiConfiguration configuration1 = new WifiConfiguration();
         configuration1.SSID = TEST_SSID;
         configuration1.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
         WifiNetworkSuggestion suggestion1 =
-                new WifiNetworkSuggestion(configuration1, null, false, false, true, true);
+                new WifiNetworkSuggestion(configuration1, null, false, false, true, true,
+                        DEFAULT_PRIORITY_GROUP);
 
         assertNotEquals(suggestion, suggestion1);
     }