Merge changes I7641cf9e,I1756add0,I94f35f9a,Ia1343e4c

* changes:
  DataChangedJournal.forEach(): Fix fragile loop condition.
  DataChangedJournal: Disallow null files/directories.
  Fix DataChangedJournalTest.equals().
  Log only a summary 'Found stale backup journal' message.
diff --git a/apct-tests/perftests/contentcapture/Android.bp b/apct-tests/perftests/contentcapture/Android.bp
new file mode 100644
index 0000000..66d7348
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/Android.bp
@@ -0,0 +1,28 @@
+// 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: "ContentCapturePerfTests",
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+    static_libs: [
+        "androidx.test.rules",
+        "androidx.annotation_annotation",
+        "apct-perftests-utils",
+        "collector-device-lib-platform",
+        "compatibility-device-util-axt",
+    ],
+    platform_apis: true,
+    test_suites: ["device-tests"],
+}
diff --git a/apct-tests/perftests/contentcapture/AndroidManifest.xml b/apct-tests/perftests/contentcapture/AndroidManifest.xml
new file mode 100644
index 0000000..ee5577f
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?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.perftests.contentcapture">
+
+    <uses-sdk android:targetSdkVersion="28" />
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.REAL_GET_TASKS" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.view.contentcapture.CustomTestActivity"
+                android:exported="true">
+        </activity>
+
+        <service
+            android:name="android.view.contentcapture.MyContentCaptureService"
+            android:label="PERF ContentCaptureService"
+            android:permission="android.permission.BIND_CONTENT_CAPTURE_SERVICE"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.service.contentcapture.ContentCaptureService" />
+            </intent-filter>
+        </service>
+
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.perftests.contentcapture" />
+</manifest>
diff --git a/apct-tests/perftests/contentcapture/AndroidTest.xml b/apct-tests/perftests/contentcapture/AndroidTest.xml
new file mode 100644
index 0000000..d2386bb
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs ContentCapturePerfTests metric instrumentation.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-metric-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="ContentCapturePerfTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.perftests.contentcapture" />
+    </test>
+</configuration>
diff --git a/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml b/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml
new file mode 100644
index 0000000..ca1a11a
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/res/layout/test_container_activity.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:tools="http://schemas.android.com/tools"
+              android:id="@+id/root_view"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:focusable="true"
+              android:focusableInTouchMode="true"
+              android:orientation="vertical" >
+</LinearLayout>
diff --git a/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml b/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml
new file mode 100644
index 0000000..9bab32c
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/res/layout/test_login_activity.xml
@@ -0,0 +1,50 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:tools="http://schemas.android.com/tools"
+              android:id="@+id/root_view"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:focusable="true"
+              android:focusableInTouchMode="true"
+              android:orientation="vertical" >
+
+    <TextView
+        android:id="@+id/username_label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Username" />
+
+    <EditText
+        android:id="@+id/username"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <TextView
+        android:id="@+id/password_label"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Password" />
+
+    <EditText
+        android:id="@+id/password"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:inputType="textPassword" />
+
+</LinearLayout>
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
new file mode 100644
index 0000000..f02c96d
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static android.view.contentcapture.CustomTestActivity.INTENT_EXTRA_CUSTOM_VIEWS;
+import static android.view.contentcapture.CustomTestActivity.INTENT_EXTRA_LAYOUT_ID;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+
+import android.app.Application;
+import android.content.ContentCaptureOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
+import android.os.UserHandle;
+import android.perftests.utils.PerfStatusReporter;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.compatibility.common.util.ActivitiesWatcher;
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.perftests.contentcapture.R;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
+import org.junit.runners.model.Statement;
+
+/**
+ * Base class for all content capture tests.
+ */
+@LargeTest
+public abstract class AbstractContentCapturePerfTestCase {
+
+    private static final String TAG = AbstractContentCapturePerfTestCase.class.getSimpleName();
+    private static final long GENERIC_TIMEOUT_MS = 10_000;
+
+    private static int sOriginalStayOnWhilePluggedIn;
+    private static Context sContext = getInstrumentation().getTargetContext();
+
+    protected ActivitiesWatcher mActivitiesWatcher;
+
+    private MyContentCaptureService.ServiceWatcher mServiceWatcher;
+
+    @Rule
+    public ActivityTestRule<CustomTestActivity> mActivityRule =
+            new ActivityTestRule<>(CustomTestActivity.class, false, false);
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Rule
+    public TestRule mServiceDisablerRule = (base, description) -> {
+        return new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                try {
+                    base.evaluate();
+                } finally {
+                    Log.v(TAG, "@mServiceDisablerRule: safelyDisableService()");
+                    safelyDisableService();
+                }
+            }
+        };
+    };
+
+    private void safelyDisableService() {
+        try {
+            resetService();
+            MyContentCaptureService.resetStaticState();
+
+            if (mServiceWatcher != null) {
+                mServiceWatcher.waitOnDestroy();
+            }
+        } catch (Throwable t) {
+            Log.e(TAG, "error disabling service", t);
+        }
+    }
+
+    /**
+     * Sets the content capture service.
+     */
+    private static void setService(@NonNull String service) {
+        final int userId = getCurrentUserId();
+        Log.d(TAG, "Setting service for user " + userId + " to " + service);
+        // TODO(b/123540602): use @TestingAPI to get max duration constant
+        runShellCommand("cmd content_capture set temporary-service %d %s 119000", userId, service);
+    }
+
+    /**
+     * Resets the content capture service.
+     */
+    private static void resetService() {
+        final int userId = getCurrentUserId();
+        Log.d(TAG, "Resetting back user " + userId + " to default service");
+        runShellCommand("cmd content_capture set temporary-service %d", userId);
+    }
+
+    private static int getCurrentUserId() {
+        return UserHandle.myUserId();
+    }
+
+    @BeforeClass
+    public static void setStayAwake() {
+        Log.v(TAG, "@BeforeClass: setStayAwake()");
+        // Some test cases will restart the activity, and stay awake is necessary to ensure that
+        // the test will not screen off during the test.
+        // Keeping the activity screen on is not enough, screen off may occur between the activity
+        // finished and the next start
+        final int stayOnWhilePluggedIn = Settings.Global.getInt(sContext.getContentResolver(),
+                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
+        sOriginalStayOnWhilePluggedIn = -1;
+        if (stayOnWhilePluggedIn != BatteryManager.BATTERY_PLUGGED_ANY) {
+            sOriginalStayOnWhilePluggedIn = stayOnWhilePluggedIn;
+            // Keep the device awake during testing.
+            setStayOnWhilePluggedIn(BatteryManager.BATTERY_PLUGGED_ANY);
+        }
+    }
+
+    @AfterClass
+    public static void resetStayAwake() {
+        Log.v(TAG, "@AfterClass: resetStayAwake()");
+        if (sOriginalStayOnWhilePluggedIn != -1) {
+            setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn);
+        }
+    }
+
+    private static void setStayOnWhilePluggedIn(int value) {
+        runShellCommand(String.format("settings put global %s %d",
+                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value));
+    }
+
+    @BeforeClass
+    public static void setAllowSelf() {
+        final ContentCaptureOptions options = new ContentCaptureOptions(null);
+        Log.v(TAG, "@BeforeClass: setAllowSelf(): options=" + options);
+        sContext.getApplicationContext().setContentCaptureOptions(options);
+    }
+
+    @AfterClass
+    public static void unsetAllowSelf() {
+        Log.v(TAG, "@AfterClass: unsetAllowSelf()");
+        clearOptions();
+    }
+
+    protected static void clearOptions() {
+        sContext.getApplicationContext().setContentCaptureOptions(null);
+    }
+
+    @BeforeClass
+    public static void disableDefaultService() {
+        Log.v(TAG, "@BeforeClass: disableDefaultService()");
+        setDefaultServiceEnabled(false);
+    }
+
+    @AfterClass
+    public static void enableDefaultService() {
+        Log.v(TAG, "@AfterClass: enableDefaultService()");
+        setDefaultServiceEnabled(true);
+    }
+
+    /**
+     * Enables / disables the default service.
+     */
+    private static void setDefaultServiceEnabled(boolean enabled) {
+        final int userId = getCurrentUserId();
+        Log.d(TAG, "setDefaultServiceEnabled(user=" + userId + ", enabled= " + enabled + ")");
+        runShellCommand("cmd content_capture set default-service-enabled %d %s", userId,
+                Boolean.toString(enabled));
+    }
+
+    @Before
+    public void prepareDevice() throws Exception {
+        Log.v(TAG, "@Before: prepareDevice()");
+
+        // Unlock screen.
+        runShellCommand("input keyevent KEYCODE_WAKEUP");
+
+        // Dismiss keyguard, in case it's set as "Swipe to unlock".
+        runShellCommand("wm dismiss-keyguard");
+
+        // Collapse notifications.
+        runShellCommand("cmd statusbar collapse");
+    }
+
+    @Before
+    public void registerLifecycleCallback() {
+        Log.v(TAG, "@Before: Registering lifecycle callback");
+        final Application app = (Application) sContext.getApplicationContext();
+        mActivitiesWatcher = new ActivitiesWatcher(GENERIC_TIMEOUT_MS);
+        app.registerActivityLifecycleCallbacks(mActivitiesWatcher);
+    }
+
+    @After
+    public void unregisterLifecycleCallback() {
+        Log.d(TAG, "@After: Unregistering lifecycle callback: " + mActivitiesWatcher);
+        if (mActivitiesWatcher != null) {
+            final Application app = (Application) sContext.getApplicationContext();
+            app.unregisterActivityLifecycleCallbacks(mActivitiesWatcher);
+        }
+    }
+
+    /**
+     * Sets {@link MyContentCaptureService} as the service for the current user and waits until
+     * its created, then add the perf test package into allow list.
+     */
+    public MyContentCaptureService enableService() throws InterruptedException {
+        if (mServiceWatcher != null) {
+            throw new IllegalStateException("There Can Be Only One!");
+        }
+
+        mServiceWatcher = MyContentCaptureService.setServiceWatcher();
+        setService(MyContentCaptureService.SERVICE_NAME);
+        mServiceWatcher.setAllowSelf();
+        return mServiceWatcher.waitOnCreate();
+    }
+
+    @NonNull
+    protected ActivityWatcher startWatcher() {
+        return mActivitiesWatcher.watch(CustomTestActivity.class);
+    }
+
+    /**
+     * Launch test activity with default login layout
+     */
+    protected CustomTestActivity launchActivity() {
+        return launchActivity(R.layout.test_login_activity, 0);
+    }
+
+    /**
+     * Launch test activity with give layout and parameter
+     */
+    protected CustomTestActivity launchActivity(int layoutId, int numViews) {
+        final Intent intent = new Intent(sContext, CustomTestActivity.class);
+        intent.putExtra(INTENT_EXTRA_LAYOUT_ID, layoutId);
+        intent.putExtra(INTENT_EXTRA_CUSTOM_VIEWS, numViews);
+        return mActivityRule.launchActivity(intent);
+    }
+
+    protected void finishActivity() {
+        try {
+            mActivityRule.finishActivity();
+        } catch (IllegalStateException e) {
+            // no op
+        }
+    }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java
new file mode 100644
index 0000000..e509837
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java
@@ -0,0 +1,88 @@
+/*
+ * 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 android.view.contentcapture;
+
+import android.app.Activity;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.perftests.contentcapture.R;
+
+/**
+ * A simple activity used for testing, e.g. performance of activity switching, or as a base
+ * container of testing view.
+ */
+public class CustomTestActivity extends Activity {
+    public static final String INTENT_EXTRA_LAYOUT_ID = "layout_id";
+    public static final String INTENT_EXTRA_CUSTOM_VIEWS = "custom_view_number";
+    public static final int MAX_VIEWS = 500;
+    private static final int CUSTOM_CONTAINER_LAYOUT_ID = R.layout.test_container_activity;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (getIntent().hasExtra(INTENT_EXTRA_LAYOUT_ID)) {
+            final int layoutId = getIntent().getIntExtra(INTENT_EXTRA_LAYOUT_ID,
+                    /* defaultValue= */0);
+            setContentView(layoutId);
+            if (layoutId == CUSTOM_CONTAINER_LAYOUT_ID) {
+                createCustomViews(findViewById(R.id.root_view),
+                        getIntent().getIntExtra(INTENT_EXTRA_CUSTOM_VIEWS, MAX_VIEWS));
+            }
+        }
+    }
+
+    private void createCustomViews(LinearLayout root, int number) {
+        LinearLayout horizontalLayout = null;
+        for (int i = 0; i < number; i++) {
+            final int j = i % 8;
+            if (horizontalLayout != null && j == 0) {
+                root.addView(horizontalLayout);
+                horizontalLayout = null;
+            }
+            if (horizontalLayout == null) {
+                horizontalLayout = createHorizontalLayout();
+            }
+            horizontalLayout.addView(createItem(null, i));
+        }
+        if (horizontalLayout != null) {
+            root.addView(horizontalLayout);
+        }
+    }
+
+    private LinearLayout createHorizontalLayout() {
+        final LinearLayout layout = new LinearLayout(getApplicationContext());
+        layout.setOrientation(LinearLayout.HORIZONTAL);
+        return layout;
+    }
+
+    private LinearLayout createItem(Drawable drawable, int index) {
+        final LinearLayout group = new LinearLayout(getApplicationContext());
+        group.setOrientation(LinearLayout.VERTICAL);
+        group.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+                LinearLayout.LayoutParams.WRAP_CONTENT, /* weight= */ 1.0f));
+
+        final TextView text = new TextView(this);
+        text.setText("i = " + index);
+        group.addView(text);
+
+        return group;
+    }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
new file mode 100644
index 0000000..750d3b4
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.CREATED;
+import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED;
+
+import android.perftests.utils.BenchmarkState;
+import android.view.View;
+
+import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher;
+import com.android.perftests.contentcapture.R;
+
+import org.junit.Test;
+
+public class LoginTest extends AbstractContentCapturePerfTestCase {
+
+    @Test
+    public void testLaunchActivity() throws Throwable {
+        enableService();
+
+        testActivityLaunchTime(R.layout.test_login_activity, 0);
+    }
+
+    @Test
+    public void testLaunchActivity_contain100Views() throws Throwable {
+        enableService();
+
+        testActivityLaunchTime(R.layout.test_container_activity, 100);
+    }
+
+    @Test
+    public void testLaunchActivity_contain300Views() throws Throwable {
+        enableService();
+
+        testActivityLaunchTime(R.layout.test_container_activity, 300);
+    }
+
+    @Test
+    public void testLaunchActivity_contain500Views() throws Throwable {
+        enableService();
+
+        testActivityLaunchTime(R.layout.test_container_activity, 500);
+    }
+
+    @Test
+    public void testLaunchActivity_noService() throws Throwable {
+        testActivityLaunchTime(R.layout.test_login_activity, 0);
+    }
+
+    @Test
+    public void testLaunchActivity_noService_contain100Views() throws Throwable {
+        testActivityLaunchTime(R.layout.test_container_activity, 100);
+    }
+
+    @Test
+    public void testLaunchActivity_noService_contain300Views() throws Throwable {
+        testActivityLaunchTime(R.layout.test_container_activity, 300);
+    }
+
+    @Test
+    public void testLaunchActivity_noService_contain500Views() throws Throwable {
+        testActivityLaunchTime(R.layout.test_container_activity, 500);
+    }
+
+    private void testActivityLaunchTime(int layoutId, int numViews) throws Throwable {
+        final ActivityWatcher watcher = startWatcher();
+
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            launchActivity(layoutId, numViews);
+
+            // Ignore the time to finish the activity
+            state.pauseTiming();
+            watcher.waitFor(CREATED);
+            finishActivity();
+            watcher.waitFor(DESTROYED);
+            state.resumeTiming();
+        }
+    }
+
+    @Test
+    public void testOnVisibilityAggregated_visibleChanged() throws Throwable {
+        enableService();
+        final CustomTestActivity activity = launchActivity();
+        final View root = activity.getWindow().getDecorView();
+        final View username = root.findViewById(R.id.username);
+
+        testOnVisibilityAggregated(username);
+    }
+
+    @Test
+    public void testOnVisibilityAggregated_visibleChanged_noService() throws Throwable {
+        final CustomTestActivity activity = launchActivity();
+        final View root = activity.getWindow().getDecorView();
+        final View username = root.findViewById(R.id.username);
+
+        testOnVisibilityAggregated(username);
+    }
+
+    @Test
+    public void testOnVisibilityAggregated_visibleChanged_noOptions() throws Throwable {
+        enableService();
+        clearOptions();
+        final CustomTestActivity activity = launchActivity();
+        final View root = activity.getWindow().getDecorView();
+        final View username = root.findViewById(R.id.username);
+
+        testOnVisibilityAggregated(username);
+    }
+
+    @Test
+    public void testOnVisibilityAggregated_visibleChanged_notImportant() throws Throwable {
+        enableService();
+        final CustomTestActivity activity = launchActivity();
+        final View root = activity.getWindow().getDecorView();
+        final View username = root.findViewById(R.id.username);
+        username.setImportantForContentCapture(View.IMPORTANT_FOR_CONTENT_CAPTURE_NO);
+
+        testOnVisibilityAggregated(username);
+    }
+
+    private void testOnVisibilityAggregated(View view) throws Throwable {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        while (state.keepRunning()) {
+            // Only count the time of onVisibilityAggregated()
+            state.pauseTiming();
+            mActivityRule.runOnUiThread(() -> {
+                state.resumeTiming();
+                view.onVisibilityAggregated(false);
+                state.pauseTiming();
+            });
+            mActivityRule.runOnUiThread(() -> {
+                state.resumeTiming();
+                view.onVisibilityAggregated(true);
+                state.pauseTiming();
+            });
+            state.resumeTiming();
+        }
+    }
+}
diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
new file mode 100644
index 0000000..b1dbb28
--- /dev/null
+++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/MyContentCaptureService.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.view.contentcapture;
+
+import android.content.ComponentName;
+import android.service.contentcapture.ActivityEvent;
+import android.service.contentcapture.ContentCaptureService;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Pair;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class MyContentCaptureService extends ContentCaptureService {
+
+    private static final String TAG = MyContentCaptureService.class.getSimpleName();
+    private static final String MY_PACKAGE = "com.android.perftests.contentcapture";
+    public static final String SERVICE_NAME = MY_PACKAGE + "/"
+            + MyContentCaptureService.class.getName();
+
+    private static ServiceWatcher sServiceWatcher;
+
+    @NonNull
+    public static ServiceWatcher setServiceWatcher() {
+        if (sServiceWatcher != null) {
+            throw new IllegalStateException("There Can Be Only One!");
+        }
+        sServiceWatcher = new ServiceWatcher();
+        return sServiceWatcher;
+    }
+
+    public static void resetStaticState() {
+        sServiceWatcher = null;
+    }
+
+    public static void clearServiceWatcher() {
+        if (sServiceWatcher != null) {
+            if (sServiceWatcher.mReadyToClear) {
+                sServiceWatcher.mService = null;
+                sServiceWatcher = null;
+            } else {
+                sServiceWatcher.mReadyToClear = true;
+            }
+        }
+    }
+
+    @Override
+    public void onConnected() {
+        Log.i(TAG, "onConnected: sServiceWatcher=" + sServiceWatcher);
+
+        if (sServiceWatcher == null) {
+            Log.e(TAG, "onConnected() without a watcher");
+            return;
+        }
+
+        if (!sServiceWatcher.mReadyToClear && sServiceWatcher.mService != null) {
+            Log.e(TAG, "onConnected(): already created: " + sServiceWatcher);
+            return;
+        }
+
+        sServiceWatcher.mService = this;
+        sServiceWatcher.mCreated.countDown();
+        sServiceWatcher.mReadyToClear = false;
+    }
+
+    @Override
+    public void onDisconnected() {
+        Log.i(TAG, "onDisconnected: sServiceWatcher=" + sServiceWatcher);
+
+        if (sServiceWatcher == null) {
+            Log.e(TAG, "onDisconnected() without a watcher");
+            return;
+        }
+        if (sServiceWatcher.mService == null) {
+            Log.e(TAG, "onDisconnected(): no service on " + sServiceWatcher);
+            return;
+        }
+
+        sServiceWatcher.mDestroyed.countDown();
+        clearServiceWatcher();
+    }
+
+    @Override
+    public void onCreateContentCaptureSession(ContentCaptureContext context,
+            ContentCaptureSessionId sessionId) {
+        Log.i(TAG, "onCreateContentCaptureSession(ctx=" + context + ", session=" + sessionId);
+    }
+
+    @Override
+    public void onDestroyContentCaptureSession(ContentCaptureSessionId sessionId) {
+        Log.i(TAG, "onDestroyContentCaptureSession(session=" + sessionId + ")");
+    }
+
+    @Override
+    public void onContentCaptureEvent(ContentCaptureSessionId sessionId,
+            ContentCaptureEvent event) {
+        Log.i(TAG, "onContentCaptureEventsRequest(session=" + sessionId + "): " + event);
+    }
+
+    @Override
+    public void onActivityEvent(ActivityEvent event) {
+        Log.i(TAG, "onActivityEvent(): " + event);
+    }
+
+    public static final class ServiceWatcher {
+
+        private static final long GENERIC_TIMEOUT_MS = 10_000;
+        private final CountDownLatch mCreated = new CountDownLatch(1);
+        private final CountDownLatch mDestroyed = new CountDownLatch(1);
+        private boolean mReadyToClear = true;
+        private Pair<Set<String>, Set<ComponentName>> mAllowList;
+
+        private MyContentCaptureService mService;
+
+        @NonNull
+        public MyContentCaptureService waitOnCreate() throws InterruptedException {
+            await(mCreated, "not created");
+
+            if (mService == null) {
+                throw new IllegalStateException("not created");
+            }
+
+            if (mAllowList != null) {
+                Log.d(TAG, "Allow after created: " + mAllowList);
+                mService.setContentCaptureWhitelist(mAllowList.first, mAllowList.second);
+            }
+
+            return mService;
+        }
+
+        public void waitOnDestroy() throws InterruptedException {
+            await(mDestroyed, "not destroyed");
+        }
+
+        /**
+         * Allow just this package.
+         */
+        public void setAllowSelf() {
+            final ArraySet<String> pkgs = new ArraySet<>(1);
+            pkgs.add(MY_PACKAGE);
+            mAllowList = new Pair<>(pkgs, null);
+        }
+
+        @Override
+        public String toString() {
+            return "mService: " + mService + " created: " + (mCreated.getCount() == 0)
+                    + " destroyed: " + (mDestroyed.getCount() == 0);
+        }
+
+        /**
+         * Awaits for a latch to be counted down.
+         */
+        private static void await(@NonNull CountDownLatch latch, @NonNull String fmt,
+                @Nullable Object... args)
+                throws InterruptedException {
+            final boolean called = latch.await(GENERIC_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            if (!called) {
+                throw new IllegalStateException(String.format(fmt, args)
+                        + " in " + GENERIC_TIMEOUT_MS + "ms");
+            }
+        }
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java b/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
index abd39f8..860c134 100644
--- a/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
+++ b/apct-tests/perftests/core/src/android/view/CutoutSpecificationBenchmark.java
@@ -16,24 +16,20 @@
 
 package android.view;
 
-import android.content.Context;
 import android.graphics.Matrix;
 import android.graphics.Path;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
 import android.text.TextUtils;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.PathParser;
 
 import androidx.benchmark.BenchmarkState;
 import androidx.benchmark.junit4.BenchmarkRule;
 import androidx.test.filters.LargeTest;
-import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -41,6 +37,10 @@
 @RunWith(AndroidJUnit4.class)
 @LargeTest
 public class CutoutSpecificationBenchmark {
+    private static final int DISPLAY_WIDTH = 1080;
+    private static final int DISPLAY_HEIGHT = 1920;
+    private static final float DISPLAY_DENSITY = 3.5f;
+
     private static final String TAG = "CutoutSpecificationBenchmark";
 
     private static final String BOTTOM_MARKER = "@bottom";
@@ -69,21 +69,6 @@
     @Rule
     public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
 
-    private Context mContext;
-    private DisplayMetrics mDisplayMetrics;
-
-    /**
-     * Setup the necessary member field used by test methods.
-     */
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
-        mDisplayMetrics = new DisplayMetrics();
-        mContext.getDisplay().getRealMetrics(mDisplayMetrics);
-    }
-
-
     private static void toRectAndAddToRegion(Path p, Region inoutRegion, Rect inoutRect) {
         final RectF rectF = new RectF();
         p.computeBounds(rectF, false /* unused */);
@@ -170,8 +155,8 @@
     public void parseByOldMethodForDoubleCutout() {
         final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
-            oldMethodParsingSpec(DOUBLE_CUTOUT_SPEC, mDisplayMetrics.widthPixels,
-                    mDisplayMetrics.heightPixels, mDisplayMetrics.density);
+            oldMethodParsingSpec(DOUBLE_CUTOUT_SPEC, DISPLAY_WIDTH, DISPLAY_HEIGHT,
+                    DISPLAY_DENSITY);
         }
     }
 
@@ -179,8 +164,7 @@
     public void parseByNewMethodForDoubleCutout() {
         final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
-            new CutoutSpecification.Parser(mDisplayMetrics.density,
-                    mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels)
+            new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
                     .parse(DOUBLE_CUTOUT_SPEC);
         }
     }
@@ -211,8 +195,8 @@
 
         final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
-            new CutoutSpecification.Parser(mDisplayMetrics.density,
-                    mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec);
+            new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
+                    .parse(spec);
         }
     }
 
@@ -233,8 +217,8 @@
 
         final BenchmarkState state = mBenchmarkRule.getState();
         while (state.keepRunning()) {
-            new CutoutSpecification.Parser(mDisplayMetrics.density,
-                    mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels).parse(spec);
+            new CutoutSpecification.Parser(DISPLAY_DENSITY, DISPLAY_WIDTH, DISPLAY_HEIGHT)
+                    .parse(spec);
         }
     }
 }
diff --git a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
index 6475f57..18643ed 100644
--- a/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/DeviceIdleInternal.java
@@ -37,7 +37,7 @@
             String reason);
 
     // duration in milliseconds
-    long getNotificationWhitelistDuration();
+    long getNotificationAllowlistDuration();
 
     void setJobsActive(boolean active);
 
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index f26549c..b1bafee 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -890,7 +890,8 @@
                 "mms_temp_app_whitelist_duration";
         private static final String KEY_SMS_TEMP_APP_WHITELIST_DURATION =
                 "sms_temp_app_whitelist_duration";
-        private static final String KEY_NOTIFICATION_WHITELIST_DURATION =
+        // TODO(b/124466289): update value to match the name
+        private static final String KEY_NOTIFICATION_ALLOWLIST_DURATION =
                 "notification_whitelist_duration";
         /**
          * Whether to wait for the user to unlock the device before causing screen-on to
@@ -1124,9 +1125,9 @@
          * Amount of time we would like to whitelist an app that is handling a
          * {@link android.app.PendingIntent} triggered by a {@link android.app.Notification}.
          * @see Settings.Global#DEVICE_IDLE_CONSTANTS
-         * @see #KEY_NOTIFICATION_WHITELIST_DURATION
+         * @see #KEY_NOTIFICATION_ALLOWLIST_DURATION
          */
-        public long NOTIFICATION_WHITELIST_DURATION;
+        public long NOTIFICATION_ALLOWLIST_DURATION;
 
         /**
          * Pre idle time factor use to make idle delay longer
@@ -1230,8 +1231,8 @@
                         KEY_MMS_TEMP_APP_WHITELIST_DURATION, 60 * 1000L);
                 SMS_TEMP_APP_WHITELIST_DURATION = mParser.getDurationMillis(
                         KEY_SMS_TEMP_APP_WHITELIST_DURATION, 20 * 1000L);
-                NOTIFICATION_WHITELIST_DURATION = mParser.getDurationMillis(
-                        KEY_NOTIFICATION_WHITELIST_DURATION, 30 * 1000L);
+                NOTIFICATION_ALLOWLIST_DURATION = mParser.getDurationMillis(
+                        KEY_NOTIFICATION_ALLOWLIST_DURATION, 30 * 1000L);
                 WAIT_FOR_UNLOCK = mParser.getBoolean(KEY_WAIT_FOR_UNLOCK, true);
                 PRE_IDLE_FACTOR_LONG = mParser.getFloat(KEY_PRE_IDLE_FACTOR_LONG, 1.67f);
                 PRE_IDLE_FACTOR_SHORT = mParser.getFloat(KEY_PRE_IDLE_FACTOR_SHORT, 0.33f);
@@ -1343,8 +1344,8 @@
             TimeUtils.formatDuration(SMS_TEMP_APP_WHITELIST_DURATION, pw);
             pw.println();
 
-            pw.print("    "); pw.print(KEY_NOTIFICATION_WHITELIST_DURATION); pw.print("=");
-            TimeUtils.formatDuration(NOTIFICATION_WHITELIST_DURATION, pw);
+            pw.print("    "); pw.print(KEY_NOTIFICATION_ALLOWLIST_DURATION); pw.print("=");
+            TimeUtils.formatDuration(NOTIFICATION_ALLOWLIST_DURATION, pw);
             pw.println();
 
             pw.print("    "); pw.print(KEY_WAIT_FOR_UNLOCK); pw.print("=");
@@ -1792,8 +1793,8 @@
 
         // duration in milliseconds
         @Override
-        public long getNotificationWhitelistDuration() {
-            return mConstants.NOTIFICATION_WHITELIST_DURATION;
+        public long getNotificationAllowlistDuration() {
+            return mConstants.NOTIFICATION_ALLOWLIST_DURATION;
         }
 
         @Override
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 360e44f..c91c39a 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -5,6 +5,10 @@
     field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
   }
 
+  public class NotificationManager {
+    method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle);
+  }
+
 }
 
 package android.content.rollback {
@@ -69,6 +73,10 @@
 
 package android.os {
 
+  public class Binder implements android.os.IBinder {
+    method public final void markVintfStability();
+  }
+
   public class StatsFrameworkInitializer {
     method public static void registerServiceWrappers();
     method public static void setStatsServiceManager(@NonNull android.os.StatsServiceManager);
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 881cfa3..e43c7d4 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -417,9 +417,6 @@
 android.app.IProcessObserver$Stub$Proxy
 android.app.IProcessObserver$Stub
 android.app.IProcessObserver
-android.app.IRequestFinishCallback$Stub$Proxy
-android.app.IRequestFinishCallback$Stub
-android.app.IRequestFinishCallback
 android.app.ISearchManager$Stub$Proxy
 android.app.ISearchManager$Stub
 android.app.ISearchManager
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 107fb263..4a982dd 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -150,7 +150,6 @@
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -3798,22 +3797,6 @@
         return false;
     }
 
-    private static final class RequestFinishCallback extends IRequestFinishCallback.Stub {
-        private final WeakReference<Activity> mActivityRef;
-
-        RequestFinishCallback(WeakReference<Activity> activityRef) {
-            mActivityRef = activityRef;
-        }
-
-        @Override
-        public void requestFinish() {
-            Activity activity = mActivityRef.get();
-            if (activity != null) {
-                activity.mHandler.post(activity::finishAfterTransition);
-            }
-        }
-    }
-
     /**
      * Called when the activity has detected the user's press of the back
      * key.  The default implementation simply finishes the current activity,
@@ -3837,9 +3820,8 @@
         try {
             // Inform activity task manager that the activity received a back press
             // while at the root of the task. This call allows ActivityTaskManager
-            // to intercept or defer finishing.
-            ActivityTaskManager.getService().onBackPressedOnTaskRoot(mToken,
-                    new RequestFinishCallback(new WeakReference<>(this)));
+            // to intercept or move the task to the back.
+            ActivityTaskManager.getService().onBackPressedOnTaskRoot(mToken);
         } catch (RemoteException e) {
             finishAfterTransition();
         }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index cba2d99..a88c6a8 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3733,7 +3733,8 @@
      * manner, excessive calls to this API could result a {@link java.lang.RuntimeException}.
      * </p>
      *
-     * @param state The state data
+     * @param state The state data. To be advised, <b>DO NOT</b> include sensitive information/data
+     * (PII, SPII, or other sensitive user data) here. Maximum length is 128 bytes.
      */
     public void setProcessStateSummary(@Nullable byte[] state) {
         try {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 9e15c1f..ef30cb4 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -318,12 +318,14 @@
             int uid, int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
             String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
-            @UserIdInt int userId, boolean allowBackgroundActivityStarts);
+            @UserIdInt int userId, boolean allowBackgroundActivityStarts,
+            @Nullable IBinder backgroundActivityStartsToken);
 
     public abstract ComponentName startServiceInPackage(int uid, Intent service,
             String resolvedType, boolean fgRequired, String callingPackage,
             @Nullable String callingFeatureId, @UserIdInt int userId,
-            boolean allowBackgroundActivityStarts) throws TransactionTooLargeException;
+            boolean allowBackgroundActivityStarts,
+            @Nullable IBinder backgroundActivityStartsToken) throws TransactionTooLargeException;
 
     public abstract void disconnectActivityFromServices(Object connectionHolder);
     public abstract void cleanUpServices(@UserIdInt int userId, ComponentName component,
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index f428b48..72a3637d 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -26,7 +26,6 @@
 import android.app.IAssistDataReceiver;
 import android.app.IInstrumentationWatcher;
 import android.app.IProcessObserver;
-import android.app.IRequestFinishCallback;
 import android.app.IServiceConnection;
 import android.app.IStopUserCallback;
 import android.app.ITaskStackListener;
@@ -458,9 +457,7 @@
 
     /**
      * Reports that an Activity received a back key press when there were no additional activities
-     * on the back stack. If the Activity should be finished, the callback will be invoked. A
-     * callback is used instead of finishing the activity directly from the server such that the
-     * client may perform actions prior to finishing.
+     * on the back stack.
      */
-    void onBackPressedOnTaskRoot(in IBinder activityToken, in IRequestFinishCallback callback);
+    void onBackPressedOnTaskRoot(in IBinder activityToken);
 }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 0deef53..a970322 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -185,6 +185,7 @@
     List<ComponentName> getEnabledNotificationListeners(int userId);
     ComponentName getAllowedNotificationAssistantForUser(int userId);
     ComponentName getAllowedNotificationAssistant();
+    boolean hasEnabledNotificationListener(String packageName, int userId);
 
     @UnsupportedAppUsage
     int getZenMode();
diff --git a/core/java/android/app/IRequestFinishCallback.aidl b/core/java/android/app/IRequestFinishCallback.aidl
deleted file mode 100644
index 3270565..0000000
--- a/core/java/android/app/IRequestFinishCallback.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app;
-
-/**
- * This callback allows ActivityTaskManager to ask the calling Activity
- * to finish in response to a call to onBackPressedOnTaskRoot.
- *
- * {@hide}
- */
-oneway interface IRequestFinishCallback {
-    void requestFinish();
-}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 1dc54dd..aa6a08b 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -814,12 +814,9 @@
 
         makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths);
 
-        String libraryPermittedPath = mDataDir;
-        if (mActivityThread == null) {
-            // In a zygote context where mActivityThread is null we can't access the app data dir
-            // and including this in libraryPermittedPath would cause SELinux denials.
-            libraryPermittedPath = "";
-        }
+        // Including an inaccessible dir in libraryPermittedPath would cause SELinux denials
+        // when the loader attempts to canonicalise the path. so we don't.
+        String libraryPermittedPath = canAccessDataDir() ? mDataDir : "";
 
         if (isBundledApp) {
             // For bundled apps, add the base directory of the app (e.g.,
@@ -972,6 +969,33 @@
         }
     }
 
+    /**
+     * Return whether we can access the package's private data directory in order to be able to
+     * load code from it.
+     */
+    private boolean canAccessDataDir() {
+        // In a zygote context where mActivityThread is null we can't access the app data dir.
+        if (mActivityThread == null) {
+            return false;
+        }
+
+        // A package can access its own data directory (the common case, so short-circuit it).
+        if (Objects.equals(mPackageName, ActivityThread.currentPackageName())) {
+            return true;
+        }
+
+        // Temporarily disable logging of disk reads on the Looper thread as this is necessary -
+        // and the loader will access the directory anyway if we don't check it.
+        StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
+        try {
+            // We are constructing a classloader for a different package. It is likely,
+            // but not certain, that we can't acccess its app data dir - so check.
+            return new File(mDataDir).canExecute();
+        } finally {
+            setThreadPolicy(oldPolicy);
+        }
+    }
+
     @UnsupportedAppUsage
     public ClassLoader getClassLoader() {
         synchronized (this) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f82ab7b..68e6561 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -958,7 +958,7 @@
      *
      * @hide
      */
-    private IBinder mWhitelistToken;
+    private IBinder mAllowlistToken;
 
     /**
      * Must be set by a process to start associating tokens with Notification objects
@@ -966,7 +966,7 @@
      *
      * @hide
      */
-    static public IBinder processWhitelistToken;
+    static public IBinder processAllowlistToken;
 
     /**
      * {@link #extras} key: this is the title of the notification,
@@ -2245,12 +2245,12 @@
     {
         int version = parcel.readInt();
 
-        mWhitelistToken = parcel.readStrongBinder();
-        if (mWhitelistToken == null) {
-            mWhitelistToken = processWhitelistToken;
+        mAllowlistToken = parcel.readStrongBinder();
+        if (mAllowlistToken == null) {
+            mAllowlistToken = processAllowlistToken;
         }
         // Propagate this token to all pending intents that are unmarshalled from the parcel.
-        parcel.setClassCookie(PendingIntent.class, mWhitelistToken);
+        parcel.setClassCookie(PendingIntent.class, mAllowlistToken);
 
         when = parcel.readLong();
         creationTime = parcel.readLong();
@@ -2368,7 +2368,7 @@
      * @hide
      */
     public void cloneInto(Notification that, boolean heavy) {
-        that.mWhitelistToken = this.mWhitelistToken;
+        that.mAllowlistToken = this.mAllowlistToken;
         that.when = this.when;
         that.creationTime = this.creationTime;
         that.mSmallIcon = this.mSmallIcon;
@@ -2678,7 +2678,7 @@
     private void writeToParcelImpl(Parcel parcel, int flags) {
         parcel.writeInt(1);
 
-        parcel.writeStrongBinder(mWhitelistToken);
+        parcel.writeStrongBinder(mAllowlistToken);
         parcel.writeLong(when);
         parcel.writeLong(creationTime);
         if (mSmallIcon == null && icon != 0) {
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index eef9c02..adbe6bb 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
@@ -801,8 +802,8 @@
      *
      * <p>The name and description should only be changed if the locale changes
      * or in response to the user renaming this channel. For example, if a user has a channel
-     * named 'John Doe' that represents messages from a 'John Doe', and 'John Doe' changes his name
-     * to 'John Smith,' the channel can be renamed to match.
+     * named 'Messages' and the user changes their locale, this channel's name should be updated
+     * with the translation of 'Messages' in the new locale.
      *
      * <p>The importance of an existing channel will only be changed if the new importance is lower
      * than the current value and the user has not altered any settings on this channel.
@@ -1539,6 +1540,25 @@
         }
     }
 
+    /**
+     * Whether the given user has an enabled
+     * {@link android.service.notification.NotificationListenerService} with the given package name.
+     *
+     * @param packageName the package name of the NotificationListenerService class
+     * @param userHandle the handle of the user that set the listener
+     * @hide
+     */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+    @SuppressLint("UserHandle")
+    public boolean hasEnabledNotificationListener(@NonNull String packageName,
+            @NonNull UserHandle userHandle) {
+        INotificationManager service = getService();
+        try {
+            return service.hasEnabledNotificationListener(packageName, userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 
     private Context mContext;
 
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index 5f6befd..e990fd7 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -525,6 +525,9 @@
         if ((level & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0) {
             protLevel += "|appPredictor";
         }
+        if ((level & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0) {
+            protLevel += "|companion";
+        }
         if ((level & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0) {
             protLevel += "|retailDemo";
         }
diff --git a/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
index c708d20..a16e878 100644
--- a/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiVendorCommandListener.aidl
@@ -22,7 +22,7 @@
  *
  * @hide
  */
-interface IHdmiVendorCommandListener {
+oneway interface IHdmiVendorCommandListener {
     void onReceived(int logicalAddress, int destAddress, in byte[] operands, boolean hasVendorId);
     void onControlStateChanged(boolean enabled, int reason);
 }
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
index 5496e17..d8a2082 100644
--- a/core/java/android/metrics/LogMaker.java
+++ b/core/java/android/metrics/LogMaker.java
@@ -39,7 +39,7 @@
     /**
      * Min required eventlog line length.
      * See: android/util/cts/EventLogTest.java
-     * Size checks enforced here are intended only as sanity checks;
+     * Size limits enforced here are intended only as a precaution;
      * your logs may be truncated earlier. Please log responsibly.
      *
      * @hide
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index a0207c8..515704c 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -543,15 +543,17 @@
 
     /**
      * Mark as being built with VINTF-level stability promise. This API should
-     * only ever be invoked by the build system. It means that the interface
-     * represented by this binder is guaranteed to be kept stable for several
-     * years, and the build system also keeps snapshots of these APIs and
-     * invokes the AIDL compiler to make sure that these snapshots are
-     * backwards compatible. Instead of using this API, use an @VintfStability
-     * interface.
+     * only ever be invoked by generated code from the aidl compiler. It means
+     * that the interface represented by this binder is guaranteed to be kept
+     * stable for several years, according to the VINTF compatibility lifecycle,
+     * and the build system also keeps snapshots of these APIs and invokes the
+     * AIDL compiler to make sure that these snapshots are backwards compatible.
+     * Instead of using this API, use the @VintfStability annotation on your
+     * AIDL interface.
      *
      * @hide
      */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public final native void markVintfStability();
 
     /**
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 9b360ed..bedbba0 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -99,6 +99,35 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface ContentsFlags {}
 
+    /** @hide */
+    @IntDef(flag = true, prefix = { "PARCELABLE_STABILITY_" }, value = {
+            PARCELABLE_STABILITY_LOCAL,
+            PARCELABLE_STABILITY_VINTF,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Stability {}
+
+    /**
+     * Something that is not meant to cross compilation boundaries.
+     *
+     * Note: unlike binder/Stability.h which uses bitsets to detect stability,
+     * since we don't currently have a notion of different local locations,
+     * higher stability levels are formed at higher levels.
+     *
+     * For instance, contained entirely within system partitions.
+     * @see #getStability()
+     * @see ParcelableHolder
+     * @hide
+     */
+    public static final int PARCELABLE_STABILITY_LOCAL = 0x0000;
+    /**
+     * Something that is meant to be used between system and vendor.
+     * @see #getStability()
+     * @see ParcelableHolder
+     * @hide
+     */
+    public static final int PARCELABLE_STABILITY_VINTF = 0x0001;
+
     /**
      * Descriptor bit used with {@link #describeContents()}: indicates that
      * the Parcelable object's flattened representation includes a file descriptor.
@@ -129,8 +158,8 @@
      * @return true if this parcelable is stable.
      * @hide
      */
-    default boolean isStable() {
-        return false;
+    default @Stability int getStability() {
+        return PARCELABLE_STABILITY_LOCAL;
     }
 
     /**
diff --git a/core/java/android/os/ParcelableHolder.java b/core/java/android/os/ParcelableHolder.java
index c37a2ff..181f94b 100644
--- a/core/java/android/os/ParcelableHolder.java
+++ b/core/java/android/os/ParcelableHolder.java
@@ -37,10 +37,10 @@
      * if {@link ParcelableHolder} contains value, otherwise, both are null.
      */
     private Parcel mParcel;
-    private boolean mIsStable = false;
+    private @Parcelable.Stability int mStability = Parcelable.PARCELABLE_STABILITY_LOCAL;
 
-    public ParcelableHolder(boolean isStable) {
-        mIsStable = isStable;
+    public ParcelableHolder(@Parcelable.Stability int stability) {
+        mStability = stability;
     }
 
     private ParcelableHolder() {
@@ -50,11 +50,11 @@
     /**
      * {@link ParcelableHolder}'s stability is determined by the parcelable
      * which contains this ParcelableHolder.
-     * For more detail refer to {@link Parcelable#isStable}.
+     * For more detail refer to {@link Parcelable#getStability}.
      */
     @Override
-    public boolean isStable() {
-        return mIsStable;
+    public @Parcelable.Stability int getStability() {
+        return mStability;
     }
 
     @NonNull
@@ -81,7 +81,8 @@
      * @return {@code false} if the parcelable's stability is more unstable ParcelableHolder.
      */
     public synchronized boolean setParcelable(@Nullable Parcelable p) {
-        if (p != null && this.isStable() && !p.isStable()) {
+        // a ParcelableHolder can only hold things at its stability or higher
+        if (p != null && this.getStability() > p.getStability()) {
             return false;
         }
         mParcelable = p;
@@ -123,7 +124,7 @@
      * Read ParcelableHolder from a parcel.
      */
     public synchronized void readFromParcel(@NonNull Parcel parcel) {
-        this.mIsStable = parcel.readBoolean();
+        this.mStability = parcel.readInt();
 
         mParcelable = null;
 
@@ -145,7 +146,7 @@
 
     @Override
     public synchronized void writeToParcel(@NonNull Parcel parcel, int flags) {
-        parcel.writeBoolean(this.mIsStable);
+        parcel.writeInt(this.mStability);
 
         if (mParcel != null) {
             parcel.writeInt(mParcel.dataSize());
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index f4aa354..8a7cf60 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -310,6 +310,7 @@
                 @Nullable VibrationEffect effect) {
             mAudioAttributes = audio;
             setUsage(audio);
+            setFlags(audio);
             applyHapticFeedbackHeuristics(effect);
         }
 
@@ -366,6 +367,12 @@
             }
         }
 
+        private void setFlags(@NonNull AudioAttributes audio) {
+            if ((audio.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) {
+                mFlags |= FLAG_BYPASS_INTERRUPTION_POLICY;
+            }
+        }
+
         /**
          * Combines all of the attributes that have been set and returns a new
          * {@link VibrationAttributes} object.
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index e3cb382..ab19fa9 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -551,7 +551,7 @@
                     out.append(((ImageSpan) style[j]).getSource());
                     out.append("\">");
 
-                    // Don't output the dummy character underlying the image.
+                    // Don't output the placeholder character underlying the image.
                     i = next;
                 }
                 if (style[j] instanceof AbsoluteSizeSpan) {
diff --git a/core/java/android/text/format/Time.java b/core/java/android/text/format/Time.java
index 80c8ec8..5b5c854 100644
--- a/core/java/android/text/format/Time.java
+++ b/core/java/android/text/format/Time.java
@@ -361,7 +361,7 @@
      */
     @Override
     public String toString() {
-        // toString() uses its own TimeCalculator rather than the shared one. Otherwise crazy stuff
+        // toString() uses its own TimeCalculator rather than the shared one. Otherwise weird stuff
         // happens during debugging when the debugger calls toString().
         TimeCalculator calculator = new TimeCalculator(this.timezone);
         calculator.copyFieldsFromTime(this);
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 064bc69..713cfb4 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -311,9 +311,6 @@
         }
 
         final int count = focusables.size();
-        if (count < 2) {
-            return null;
-        }
         switch (direction) {
             case View.FOCUS_FORWARD:
                 return getNextFocusable(focused, focusables, count);
@@ -376,29 +373,29 @@
     }
 
     private static View getNextFocusable(View focused, ArrayList<View> focusables, int count) {
-        if (count < 2) {
-            return null;
-        }
         if (focused != null) {
             int position = focusables.lastIndexOf(focused);
             if (position >= 0 && position + 1 < count) {
                 return focusables.get(position + 1);
             }
         }
-        return focusables.get(0);
+        if (!focusables.isEmpty()) {
+            return focusables.get(0);
+        }
+        return null;
     }
 
     private static View getPreviousFocusable(View focused, ArrayList<View> focusables, int count) {
-        if (count < 2) {
-            return null;
-        }
         if (focused != null) {
             int position = focusables.indexOf(focused);
             if (position > 0) {
                 return focusables.get(position - 1);
             }
         }
-        return focusables.get(count - 1);
+        if (!focusables.isEmpty()) {
+            return focusables.get(count - 1);
+        }
+        return null;
     }
 
     private static View getNextKeyboardNavigationCluster(
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fe6f33d..6552e30 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4032,7 +4032,7 @@
             yOffset -= surfaceInsets.top;
 
             // Offset dirty rect for surface insets.
-            dirty.offset(surfaceInsets.left, surfaceInsets.right);
+            dirty.offset(surfaceInsets.left, surfaceInsets.top);
         }
 
         boolean accessibilityFocusDirty = false;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 1c75232..052bca5 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1511,7 +1511,7 @@
      *
      * @param hosts the list of hosts
      * @param callback will be called with {@code true} if hosts are successfully added to the
-     * whitelist. It will be called with {@code false} if any hosts are malformed. The callback
+     * allowlist. It will be called with {@code false} if any hosts are malformed. The callback
      * will be run on the UI thread
      */
     public static void setSafeBrowsingWhitelist(@NonNull List<String> hosts,
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 150fa88..7b6e1a3 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -173,8 +173,9 @@
      * when accessing private data or the view system.
      *
      * <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
-     * Browsing checks. If this is undesired, whitelist the URL with {@link
-     * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
+     * Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist}
+     * to skip Safe Browsing checks for that host or dismiss the warning in {@link
+     * #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}.
      *
      * @param view The {@link android.webkit.WebView} that is requesting the
      *             resource.
@@ -211,8 +212,9 @@
      * when accessing private data or the view system.
      *
      * <p class="note"><b>Note:</b> When Safe Browsing is enabled, these URLs still undergo Safe
-     * Browsing checks. If this is undesired, whitelist the URL with {@link
-     * WebView#setSafeBrowsingWhitelist} or ignore the warning with {@link #onSafeBrowsingHit}.
+     * Browsing checks. If this is undesired, you can use {@link WebView#setSafeBrowsingWhitelist}
+     * to skip Safe Browsing checks for that host or dismiss the warning in {@link
+     * #onSafeBrowsingHit} by calling {@link SafeBrowsingResponse#proceed}.
      *
      * @param view The {@link android.webkit.WebView} that is requesting the
      *             resource.
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index 23bbe69..89206fda 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -1142,7 +1142,7 @@
             bitmapRenderNode.setOutline(outline);
             bitmapRenderNode.setClipToOutline(true);
 
-            // Create a dummy draw, which will be replaced later with real drawing.
+            // Create a placeholder draw, which will be replaced later with real drawing.
             final RecordingCanvas canvas = bitmapRenderNode.beginRecording(
                     mContentWidth, mContentHeight);
             try {
diff --git a/core/java/com/android/internal/listeners/ListenerExecutor.java b/core/java/com/android/internal/listeners/ListenerExecutor.java
index e78e32b..9979e60 100644
--- a/core/java/com/android/internal/listeners/ListenerExecutor.java
+++ b/core/java/com/android/internal/listeners/ListenerExecutor.java
@@ -40,7 +40,7 @@
         /**
          * Called before this operation is to be run. Some operations may be canceled before they
          * are run, in which case this method may not be called. {@link #onPostExecute(boolean)}
-         * will only be run if this method was run.
+         * will only be run if this method was run. This callback is invoked on the calling thread.
          */
         default void onPreExecute() {}
 
@@ -49,7 +49,7 @@
          * RuntimeException, which will propagate normally. Implementations of
          * {@link ListenerExecutor} have the option to override
          * {@link ListenerExecutor#onOperationFailure(ListenerOperation, Exception)} instead to
-         * intercept failures at the class level.
+         * intercept failures at the class level. This callback is invoked on the executor thread.
          */
         default void onFailure(Exception e) {
             // implementations should handle any exceptions that may be thrown
@@ -59,21 +59,24 @@
         /**
          * Called after the operation is run. This method will always be called if
          * {@link #onPreExecute()} is called. Success implies that the operation was run to
-         * completion with no failures.
+         * completion with no failures. This callback may be invoked on the calling thread or
+         * executor thread.
          */
         default void onPostExecute(boolean success) {}
 
         /**
          * Called after this operation is complete (which does not imply that it was necessarily
          * run). Will always be called once per operation, no matter if the operation was run or
-         * not. Success implies that the operation was run to completion with no failures.
+         * not. Success implies that the operation was run to completion with no failures. This
+         * callback may be invoked on the calling thread or executor thread.
          */
         default void onComplete(boolean success) {}
     }
 
     /**
      * May be override to handle operation failures at a class level. Will not be invoked in the
-     * event of a RuntimeException, which will propagate normally.
+     * event of a RuntimeException, which will propagate normally. This callback is invoked on the
+     * executor thread.
      */
     default <TListener> void onOperationFailure(ListenerOperation<TListener> operation,
             Exception exception) {
@@ -83,7 +86,8 @@
     /**
      * Executes the given listener operation on the given executor, using the provided listener
      * supplier. If the supplier returns a null value, or a value during the operation that does not
-     * match the value prior to the operation, then the operation is considered canceled.
+     * match the value prior to the operation, then the operation is considered canceled. If a null
+     * operation is supplied, nothing happens.
      */
     default <TListener> void executeSafely(Executor executor, Supplier<TListener> listenerSupplier,
             @Nullable ListenerOperation<TListener> operation) {
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 957cc76..c6a1153 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -178,12 +178,6 @@
     // URL-handling state upon factory reset.
     final ArraySet<String> mLinkedApps = new ArraySet<>();
 
-    // These are the packages that are whitelisted to be able to run as system user
-    final ArraySet<String> mSystemUserWhitelistedApps = new ArraySet<>();
-
-    // These are the packages that should not run under system user
-    final ArraySet<String> mSystemUserBlacklistedApps = new ArraySet<>();
-
     // These are the components that are enabled by default as VR mode listener services.
     final ArraySet<ComponentName> mDefaultVrComponents = new ArraySet<>();
 
@@ -317,14 +311,6 @@
         return mLinkedApps;
     }
 
-    public ArraySet<String> getSystemUserWhitelistedApps() {
-        return mSystemUserWhitelistedApps;
-    }
-
-    public ArraySet<String> getSystemUserBlacklistedApps() {
-        return mSystemUserBlacklistedApps;
-    }
-
     public ArraySet<String> getHiddenApiWhitelistedApps() {
         return mHiddenApiPackageWhitelist;
     }
@@ -904,34 +890,6 @@
                         }
                         XmlUtils.skipCurrentTag(parser);
                     } break;
-                    case "system-user-whitelisted-app": {
-                        if (allowAppConfigs) {
-                            String pkgname = parser.getAttributeValue(null, "package");
-                            if (pkgname == null) {
-                                Slog.w(TAG, "<" + name + "> without package in "
-                                        + permFile + " at " + parser.getPositionDescription());
-                            } else {
-                                mSystemUserWhitelistedApps.add(pkgname);
-                            }
-                        } else {
-                            logNotAllowedInPartition(name, permFile, parser);
-                        }
-                        XmlUtils.skipCurrentTag(parser);
-                    } break;
-                    case "system-user-blacklisted-app": {
-                        if (allowAppConfigs) {
-                            String pkgname = parser.getAttributeValue(null, "package");
-                            if (pkgname == null) {
-                                Slog.w(TAG, "<" + name + "> without package in "
-                                        + permFile + " at " + parser.getPositionDescription());
-                            } else {
-                                mSystemUserBlacklistedApps.add(pkgname);
-                            }
-                        } else {
-                            logNotAllowedInPartition(name, permFile, parser);
-                        }
-                        XmlUtils.skipCurrentTag(parser);
-                    } break;
                     case "default-enabled-vr-app": {
                         if (allowAppConfigs) {
                             String pkgname = parser.getAttributeValue(null, "package");
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index fe290f3..bae3bf90 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -355,6 +355,7 @@
     <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_ALLOWED_CARRIER" />
     <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_DISALLOWED_CARRIER" />
     <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_DISMISSED" />
+    <protected-broadcast android:name="com.android.server.wifi.action.CarrierNetwork.USER_CLICKED" />
     <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.USER_DISMISSED_NOTIFICATION" />
     <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" />
     <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" />
diff --git a/core/res/res/values-mcc260/config.xml b/core/res/res/values-mcc260/config.xml
new file mode 100644
index 0000000..79eefb7
--- /dev/null
+++ b/core/res/res/values-mcc260/config.xml
@@ -0,0 +1,25 @@
+<?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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+  <!-- Set to false to disable emergency alert. -->
+  <bool name="config_cellBroadcastAppLinks">false</bool>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values-mcc262/config.xml b/core/res/res/values-mcc262/config.xml
new file mode 100644
index 0000000..79eefb7
--- /dev/null
+++ b/core/res/res/values-mcc262/config.xml
@@ -0,0 +1,25 @@
+<?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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+  <!-- Set to false to disable emergency alert. -->
+  <bool name="config_cellBroadcastAppLinks">false</bool>
+</resources>
\ No newline at end of file
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 28a164f..bc4c099 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2286,7 +2286,7 @@
     </integer-array>
 
     <!-- Set to true to add links to Cell Broadcast app from Settings and MMS app. -->
-    <bool name="config_cellBroadcastAppLinks">false</bool>
+    <bool name="config_cellBroadcastAppLinks">true</bool>
 
     <!-- The default value if the SyncStorageEngine should sync automatically or not -->
     <bool name="config_syncstorageengine_masterSyncAutomatically">true</bool>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index e1f6b2a..dd8f40d 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -263,10 +263,4 @@
          be able to connect to the internet when such a proxy is in use, since
          all outgoing connections originate from this app. -->
     <allow-in-power-save-except-idle package="com.android.proxyhandler" />
-
-    <!-- These are the packages that are white-listed to be able to run as system user -->
-    <system-user-whitelisted-app package="com.android.settings" />
-
-    <!-- These are the packages that shouldn't run as system user -->
-    <system-user-blacklisted-app package="com.android.wallpaper.livepicker" />
 </permissions>
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index d92dbb2..4cdfbb8 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -412,6 +412,7 @@
 key 583   ASSIST
 
 # Keys defined by HID usages
+key usage 0x0c0067 WINDOW
 key usage 0x0c006F BRIGHTNESS_UP
 key usage 0x0c0070 BRIGHTNESS_DOWN
 
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index d25fc4b..b2c39c9 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -139,6 +139,9 @@
         (*mGlobalData)->reportJank();
     }
 
+    if (mSwapDeadline < 0) {
+        mSwapDeadline = frame[FrameInfoIndex::IntendedVsync] + mFrameInterval;
+    }
     bool isTripleBuffered = (mSwapDeadline - frame[FrameInfoIndex::IntendedVsync]) > (mFrameInterval * 0.1);
 
     mSwapDeadline = std::max(mSwapDeadline + mFrameInterval,
diff --git a/libs/hwui/JankTracker.h b/libs/hwui/JankTracker.h
index 4460266..b3fbbfe 100644
--- a/libs/hwui/JankTracker.h
+++ b/libs/hwui/JankTracker.h
@@ -75,7 +75,7 @@
 
     std::array<int64_t, NUM_BUCKETS> mThresholds;
     int64_t mFrameInterval;
-    nsecs_t mSwapDeadline;
+    nsecs_t mSwapDeadline = -1;
     // The amount of time we will erase from the total duration to account
     // for SF vsync offsets with HWC2 blocking dequeueBuffers.
     // (Vsync + mDequeueBlockTolerance) is the point at which we expect
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index 496885c..997339e 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -151,6 +151,16 @@
         return status;
     }
 
+    /**
+     * Builds an empty GpsStatus. Should only be used for legacy reasons.
+     *
+     * @hide
+     */
+    @NonNull
+    static GpsStatus createEmpty() {
+        return new GpsStatus();
+    }
+
     private GpsStatus() {
     }
 
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index 6e7f6a5..29b483a 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -24,6 +24,6 @@
  */
 oneway interface ILocationListener
 {
-    void onLocationChanged(in Location location, in IRemoteCallback onCompleteCallback);
+    void onLocationChanged(in Location location, in @nullable IRemoteCallback onCompleteCallback);
     void onProviderEnabledChanged(String provider, boolean enabled);
 }
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index bb8f81d..0e7eaa2 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -113,7 +113,7 @@
     List<LocationRequest> getTestProviderCurrentRequests(String provider);
     LocationTime getGnssTimeMillis();
 
-    boolean sendExtraCommand(String provider, String command, inout Bundle extras);
+    void sendExtraCommand(String provider, String command, inout Bundle extras);
 
     // used by gts tests to verify whitelists
     String[] getBackgroundThrottlingWhitelist();
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 8df0834..2738ff4f 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -36,7 +36,9 @@
 public interface LocationListener {
 
     /**
-     * Called when the location has changed.
+     * Called when the location has changed. A wakelock is held on behalf on the listener for some
+     * brief amount of time as this callback executes. If this callback performs long running
+     * operations, it is the client's responsibility to obtain their own wakelock.
      *
      * @param location the updated location
      */
@@ -52,18 +54,17 @@
     default void onStatusChanged(String provider, int status, Bundle extras) {}
 
     /**
-     * Called when the provider is enabled by the user.
+     * Called when a provider this listener is registered with becomes enabled.
      *
-     * @param provider the name of the location provider that has become enabled
+     * @param provider the name of the location provider
      */
     default void onProviderEnabled(@NonNull String provider) {}
 
     /**
-     * Called when the provider is disabled by the user. If requestLocationUpdates
-     * is called on an already disabled provider, this method is called
-     * immediately.
+     * Called when the provider this listener is registered with becomes disabled. If a provider is
+     * disabled when this listener is registered, this callback will be invoked immediately.
      *
-     * @param provider the name of the location provider that has become disabled
+     * @param provider the name of the location provider
      */
     default void onProviderDisabled(@NonNull String provider) {}
 }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index c0b8e1b..1803027 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -303,7 +303,6 @@
     public static final String METADATA_SETTINGS_FOOTER_STRING =
             "com.android.settings.location.FOOTER_STRING";
 
-
     private static final long MAX_SINGLE_LOCATION_TIMEOUT_MS = 30 * 1000;
 
     @GuardedBy("sLocationListeners")
@@ -311,7 +310,9 @@
             sLocationListeners = new WeakHashMap<>();
 
     final Context mContext;
-    @UnsupportedAppUsage
+
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link "
+            + "LocationManager}")
     final ILocationManager mService;
 
     private final Object mLock = new Object();
@@ -421,8 +422,7 @@
         try {
             return mService.getExtraLocationControllerPackage();
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-            return null;
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -437,7 +437,7 @@
         try {
             mService.setExtraLocationControllerPackage(packageName);
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -452,7 +452,7 @@
         try {
             mService.setExtraLocationControllerPackageEnabled(enabled);
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -466,8 +466,7 @@
         try {
             return mService.isExtraLocationControllerPackageEnabled();
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-            return false;
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -485,7 +484,7 @@
         try {
             mService.setExtraLocationControllerPackage(packageName);
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -503,7 +502,7 @@
         try {
             mService.setExtraLocationControllerPackageEnabled(enabled);
         } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -1199,7 +1198,7 @@
                         mContext.getPackageName(), mContext.getAttributionTag(),
                         AppOpsManager.toReceiverId(listener));
             } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
+                throw e.rethrowFromSystemServer();
             }
         }
     }
@@ -1301,7 +1300,7 @@
                     // unregistration is complete.
                     mService.unregisterLocationListener(transport);
                 } catch (RemoteException e) {
-                    e.rethrowFromSystemServer();
+                    throw e.rethrowFromSystemServer();
                 }
             }
         }
@@ -1517,7 +1516,8 @@
         Preconditions.checkArgument(command != null, "invalid null command");
 
         try {
-            return mService.sendExtraCommand(provider, command, extras);
+            mService.sendExtraCommand(provider, command, extras);
+            return true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1835,6 +1835,10 @@
             } else {
                 status.setStatus(gnssStatus, ttff);
             }
+        } else if (status == null) {
+            // even though this method is marked as nullable, legacy behavior was to never return
+            // a null result, and there are applications that rely on this behavior.
+            status = GpsStatus.createEmpty();
         }
         return status;
     }
@@ -2424,7 +2428,7 @@
                 try {
                     cancellationSignal.cancel();
                 } catch (RemoteException e) {
-                    e.rethrowFromSystemServer();
+                    throw e.rethrowFromSystemServer();
                 }
             }
         }
@@ -2464,7 +2468,8 @@
         }
 
         @Override
-        public void onLocationChanged(Location location, IRemoteCallback onCompleteCallback) {
+        public void onLocationChanged(Location location,
+                @Nullable IRemoteCallback onCompleteCallback) {
             executeSafely(mExecutor, () -> mListener, new ListenerOperation<LocationListener>() {
                 @Override
                 public void operate(LocationListener listener) {
@@ -2473,7 +2478,13 @@
 
                 @Override
                 public void onComplete(boolean success) {
-                    markComplete(onCompleteCallback);
+                    if (onCompleteCallback != null) {
+                        try {
+                            onCompleteCallback.sendResult(null);
+                        } catch (RemoteException e) {
+                            throw e.rethrowFromSystemServer();
+                        }
+                    }
                 }
             });
         }
@@ -2488,14 +2499,6 @@
                 }
             });
         }
-
-        private void markComplete(IRemoteCallback onCompleteCallback) {
-            try {
-                onCompleteCallback.sendResult(null);
-            } catch (RemoteException e) {
-                e.rethrowFromSystemServer();
-            }
-        }
     }
 
     private static class NmeaAdapter extends GnssStatus.Callback implements OnNmeaMessageListener {
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 6e26f2c..3cd4081 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -337,33 +337,44 @@
     }
     mIonHandle = new C2HandleIon(dup(mAvHandle->data[0]), mDataLength);
     std::shared_ptr<C2LinearBlock> block = _C2BlockFactory::CreateLinearBlock(mIonHandle);
-
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
-    std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
-    context->mBlock = block;
-    std::shared_ptr<C2Buffer> pC2Buffer = context->toC2Buffer(0, mDataLength);
-    context->mBuffer = pC2Buffer;
-    mC2Buffer = pC2Buffer;
-    if (mAvHandle->numInts > 0) {
-        // use first int in the native_handle as the index
-        int index = mAvHandle->data[mAvHandle->numFds];
-        std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(index, mDataId);
-        std::shared_ptr<C2Info> info(std::static_pointer_cast<C2Info>(c2param));
-        pC2Buffer->setInfo(info);
+    if (block != nullptr) {
+        // CreateLinearBlock delete mIonHandle after it create block successfully.
+        // ToDo: coordinate who is response to delete mIonHandle
+        mIonHandle = NULL;
+        JNIEnv *env = AndroidRuntime::getJNIEnv();
+        std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock};
+        context->mBlock = block;
+        std::shared_ptr<C2Buffer> pC2Buffer = context->toC2Buffer(0, mDataLength);
+        context->mBuffer = pC2Buffer;
+        mC2Buffer = pC2Buffer;
+        if (mAvHandle->numInts > 0) {
+            // use first int in the native_handle as the index
+            int index = mAvHandle->data[mAvHandle->numFds];
+            std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(index, mDataId);
+            std::shared_ptr<C2Info> info(std::static_pointer_cast<C2Info>(c2param));
+            pC2Buffer->setInfo(info);
+        }
+        pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this);
+        jobject linearBlock =
+                env->NewObject(
+                        env->FindClass("android/media/MediaCodec$LinearBlock"),
+                        gFields.linearBlockInitID);
+        env->CallVoidMethod(
+                linearBlock,
+                gFields.linearBlockSetInternalStateID,
+                (jlong)context.release(),
+                true);
+        mLinearBlockObj = env->NewWeakGlobalRef(linearBlock);
+        mAvHandleRefCnt++;
+        return mLinearBlockObj;
+    } else {
+        native_handle_close(const_cast<native_handle_t*>(
+                    reinterpret_cast<const native_handle_t*>(mIonHandle)));
+        native_handle_delete(const_cast<native_handle_t*>(
+                    reinterpret_cast<const native_handle_t*>(mIonHandle)));
+        mIonHandle = NULL;
+        return NULL;
     }
-    pC2Buffer->registerOnDestroyNotify(&DestroyCallback, this);
-    jobject linearBlock =
-            env->NewObject(
-                    env->FindClass("android/media/MediaCodec$LinearBlock"),
-                    gFields.linearBlockInitID);
-    env->CallVoidMethod(
-            linearBlock,
-            gFields.linearBlockSetInternalStateID,
-            (jlong)context.release(),
-            true);
-    mLinearBlockObj = env->NewWeakGlobalRef(linearBlock);
-    mAvHandleRefCnt++;
-    return mLinearBlockObj;
 }
 
 uint64_t MediaEvent::getAudioHandle() {
diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt
index a5ca196..c1dbfb7 100644
--- a/non-updatable-api/module-lib-current.txt
+++ b/non-updatable-api/module-lib-current.txt
@@ -5,6 +5,10 @@
     field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage";
   }
 
+  public class NotificationManager {
+    method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle);
+  }
+
 }
 
 package android.content.rollback {
@@ -29,6 +33,10 @@
 
 package android.os {
 
+  public class Binder implements android.os.IBinder {
+    method public final void markVintfStability();
+  }
+
   public class StatsServiceManager {
     method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsCompanionServiceRegisterer();
     method @NonNull public android.os.StatsServiceManager.ServiceRegisterer getStatsManagerServiceRegisterer();
diff --git a/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
index 0a29424..09fbf7a 100644
--- a/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
+++ b/packages/CarSystemUI/res/layout/car_user_switching_dialog.xml
@@ -15,7 +15,6 @@
   ~ limitations under the License.
   -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:fitsSystemWindows="true"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:gravity="center"
diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
index 2dc499c..2c97889 100644
--- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
+++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml
@@ -22,12 +22,10 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <!-- TODO(b/151617493): replace marginBottom with insets. -->
     <ViewStub android:id="@+id/notification_panel_stub"
               android:layout_width="match_parent"
               android:layout_height="match_parent"
-              android:layout="@layout/notification_panel_container"
-              android:layout_marginBottom="@dimen/navigation_bar_height"/>
+              android:layout="@layout/notification_panel_container"/>
 
     <ViewStub android:id="@+id/keyguard_stub"
               android:layout_width="match_parent"
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
index 1a1b93b..03ea941 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java
@@ -32,7 +32,7 @@
     @Override
     protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
         return DaggerCarSystemUIRootComponent.builder()
-                .contextHolder(new ContextHolder(context))
+                .context(context)
                 .build();
     }
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
index 088420e..ece3bee 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
@@ -36,12 +36,17 @@
                 DependencyBinder.class,
                 PipModule.class,
                 OneHandedModule.class,
-                SystemUIFactory.ContextHolder.class,
                 SystemServicesModule.class,
                 SystemUIModule.class,
                 CarSystemUIModule.class,
                 CarSystemUIBinder.class
         })
 public interface CarSystemUIRootComponent extends SystemUIRootComponent {
-
+    /**
+     * Builder for a CarSystemUIRootComponent.
+     */
+    @Component.Builder
+    interface Builder extends SystemUIRootComponent.Builder {
+        CarSystemUIRootComponent build();
+    }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
index 69766cc..51a7245 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/keyguard/CarKeyguardViewController.java
@@ -141,6 +141,11 @@
     }
 
     @Override
+    protected boolean shouldShowNavigationBar() {
+        return true;
+    }
+
+    @Override
     public void onFinishInflate() {
         mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
                 mViewMediatorCallback, mLockPatternUtils,
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 1eead62..8d58436 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/notification/NotificationPanelViewController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.car.notification;
 
+import static android.view.WindowInsets.Type.navigationBars;
+
 import android.app.ActivityManager;
 import android.car.Car;
 import android.car.drivingstate.CarUxRestrictionsManager;
@@ -197,6 +199,16 @@
     }
 
     @Override
+    protected boolean shouldShowStatusBar() {
+        return true;
+    }
+
+    @Override
+    protected int getInsetTypesToFit() {
+        return navigationBars();
+    }
+
+    @Override
     protected boolean shouldShowHUN() {
         return mEnableHeadsUpNotificationWhenNotificationShadeOpen;
     }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
index 45f3d34..0d77c13 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
@@ -91,6 +91,11 @@
                 R.integer.config_userSwitchTransitionViewShownTimeoutMs);
     }
 
+    @Override
+    protected int getInsetTypesToFit() {
+        return 0;
+    }
+
     /**
      * Makes the user switch transition view appear and draws the content inside of it if a user
      * that is different from the previous user is provided and if the dialog is not already
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
index 3969f92..53deb9d 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewController.java
@@ -16,9 +16,12 @@
 
 package com.android.systemui.car.window;
 
+import static android.view.WindowInsets.Type.statusBars;
+
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
+import android.view.WindowInsets;
 
 /**
  * Owns a {@link View} that is present in SystemUIOverlayWindow.
@@ -140,9 +143,25 @@
     }
 
     /**
+     * Returns {@code true} if status bar should be displayed over this view.
+     */
+    protected boolean shouldShowStatusBar() {
+        return false;
+    }
+
+    /**
      * Returns {@code true} if this view should be hidden during the occluded state.
      */
     protected boolean shouldShowWhenOccluded() {
         return false;
     }
+
+    /**
+     * Returns the insets types to fit to the sysui overlay window when this
+     * {@link OverlayViewController} is in the foreground.
+     */
+    @WindowInsets.Type.InsetsType
+    protected int getInsetTypesToFit() {
+        return statusBars();
+    }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
index 8e94109..2494242 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayViewGlobalStateController.java
@@ -16,13 +16,17 @@
 
 package com.android.systemui.car.window;
 
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+
 import android.annotation.Nullable;
 import android.util.Log;
+import android.view.WindowInsets.Type.InsetsType;
+import android.view.WindowInsetsController;
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
-
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -48,10 +52,7 @@
     private static final String TAG = OverlayViewGlobalStateController.class.getSimpleName();
     private static final int UNKNOWN_Z_ORDER = -1;
     private final SystemUIOverlayWindowController mSystemUIOverlayWindowController;
-    private final CarNavigationBarController mCarNavigationBarController;
-
-    private boolean mIsOccluded;
-
+    private final WindowInsetsController mWindowInsetsController;
     @VisibleForTesting
     Map<OverlayViewController, Integer> mZOrderMap;
     @VisibleForTesting
@@ -60,14 +61,15 @@
     Set<OverlayViewController> mViewsHiddenForOcclusion;
     @VisibleForTesting
     OverlayViewController mHighestZOrder;
+    private boolean mIsOccluded;
 
     @Inject
     public OverlayViewGlobalStateController(
-            CarNavigationBarController carNavigationBarController,
             SystemUIOverlayWindowController systemUIOverlayWindowController) {
         mSystemUIOverlayWindowController = systemUIOverlayWindowController;
         mSystemUIOverlayWindowController.attach();
-        mCarNavigationBarController = carNavigationBarController;
+        mWindowInsetsController =
+                mSystemUIOverlayWindowController.getBaseLayout().getWindowInsetsController();
         mZOrderMap = new HashMap<>();
         mZOrderVisibleSortedMap = new TreeMap<>();
         mViewsHiddenForOcclusion = new HashSet<>();
@@ -115,7 +117,9 @@
         }
 
         updateInternalsWhenShowingView(viewController);
+        refreshInsetTypesToFit();
         refreshNavigationBarVisibility();
+        refreshStatusBarVisibility();
 
         Log.d(TAG, "Content shown: " + viewController.getClass().getName());
         debugLog();
@@ -185,7 +189,9 @@
 
         mZOrderVisibleSortedMap.remove(mZOrderMap.get(viewController));
         refreshHighestZOrderWhenHidingView(viewController);
+        refreshInsetTypesToFit();
         refreshNavigationBarVisibility();
+        refreshStatusBarVisibility();
 
         if (mZOrderVisibleSortedMap.isEmpty()) {
             setWindowVisible(false);
@@ -208,10 +214,28 @@
     }
 
     private void refreshNavigationBarVisibility() {
+        mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
         if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowNavigationBar()) {
-            mCarNavigationBarController.showBars();
+            mWindowInsetsController.show(navigationBars());
         } else {
-            mCarNavigationBarController.hideBars();
+            mWindowInsetsController.hide(navigationBars());
+        }
+    }
+
+    private void refreshStatusBarVisibility() {
+        mWindowInsetsController.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
+        if (mZOrderVisibleSortedMap.isEmpty() || mHighestZOrder.shouldShowStatusBar()) {
+            mWindowInsetsController.show(statusBars());
+        } else {
+            mWindowInsetsController.hide(statusBars());
+        }
+    }
+
+    private void refreshInsetTypesToFit() {
+        if (mZOrderVisibleSortedMap.isEmpty()) {
+            setFitInsetsTypes(statusBars());
+        } else {
+            setFitInsetsTypes(mHighestZOrder.getInsetTypesToFit());
         }
     }
 
@@ -224,6 +248,10 @@
         mSystemUIOverlayWindowController.setWindowVisible(visible);
     }
 
+    private void setFitInsetsTypes(@InsetsType int types) {
+        mSystemUIOverlayWindowController.setFitInsetsTypes(types);
+    }
+
     /**
      * Sets the {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM} flag of the
      * sysui overlay window.
diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
index bcd96f6..029bd37 100644
--- a/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
+++ b/packages/CarSystemUI/src/com/android/systemui/car/window/SystemUIOverlayWindowController.java
@@ -25,6 +25,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 
 import com.android.systemui.R;
@@ -99,7 +100,6 @@
                 PixelFormat.TRANSLUCENT);
         mLp.token = new Binder();
         mLp.gravity = Gravity.TOP;
-        mLp.setFitInsetsTypes(/* types= */ 0);
         mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
         mLp.setTitle("SystemUIOverlayWindow");
         mLp.packageName = mContext.getPackageName();
@@ -110,6 +110,12 @@
         setWindowVisible(false);
     }
 
+    /** Sets the types of insets to fit. Note: This should be rarely used. */
+    public void setFitInsetsTypes(@WindowInsets.Type.InsetsType int types) {
+        mLpChanged.setFitInsetsTypes(types);
+        updateWindow();
+    }
+
     /** Sets the window to the visible state. */
     public void setWindowVisible(boolean visible) {
         mVisible = visible;
diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
index 20f9bc8e..ff28665 100644
--- a/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
+++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/window/OverlayViewGlobalStateControllerTest.java
@@ -16,9 +16,14 @@
 
 package com.android.systemui.car.window;
 
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -28,19 +33,18 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
+import android.view.WindowInsetsController;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.car.CarSystemUiTest;
-import com.android.systemui.car.navigationbar.CarNavigationBarController;
 import com.android.systemui.tests.R;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.Arrays;
@@ -58,8 +62,6 @@
     private ViewGroup mBaseLayout;
 
     @Mock
-    private CarNavigationBarController mCarNavigationBarController;
-    @Mock
     private SystemUIOverlayWindowController mSystemUIOverlayWindowController;
     @Mock
     private OverlayViewMediator mOverlayViewMediator;
@@ -71,18 +73,22 @@
     private OverlayPanelViewController mOverlayPanelViewController;
     @Mock
     private Runnable mRunnable;
+    @Mock
+    private WindowInsetsController mWindowInsetsController;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(/* testClass= */ this);
 
-        mBaseLayout = (ViewGroup) LayoutInflater.from(mContext).inflate(
-                R.layout.overlay_view_global_state_controller_test, /* root= */ null);
+        mBaseLayout = spy((ViewGroup) LayoutInflater.from(mContext).inflate(
+                R.layout.overlay_view_global_state_controller_test, /* root= */ null));
+
+        when(mBaseLayout.getWindowInsetsController()).thenReturn(mWindowInsetsController);
 
         when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
 
         mOverlayViewGlobalStateController = new OverlayViewGlobalStateController(
-                mCarNavigationBarController, mSystemUIOverlayWindowController);
+                mSystemUIOverlayWindowController);
 
         verify(mSystemUIOverlayWindowController).attach();
     }
@@ -108,7 +114,7 @@
 
         mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
 
-        verify(mCarNavigationBarController).hideBars();
+        verify(mWindowInsetsController).hide(navigationBars());
     }
 
     @Test
@@ -118,7 +124,37 @@
 
         mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
 
-        verify(mCarNavigationBarController).showBars();
+        verify(mWindowInsetsController).show(navigationBars());
+    }
+
+    @Test
+    public void showView_nothingAlreadyShown_shouldShowStatusBarFalse_statusBarsHidden() {
+        setupOverlayViewController1();
+        when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+        verify(mWindowInsetsController).hide(statusBars());
+    }
+
+    @Test
+    public void showView_nothingAlreadyShown_shouldShowStatusBarTrue_statusBarsShown() {
+        setupOverlayViewController1();
+        when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+        verify(mWindowInsetsController).show(statusBars());
+    }
+
+    @Test
+    public void showView_nothingAlreadyShown_fitsNavBarInsets_insetsAdjusted() {
+        setupOverlayViewController1();
+        when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(navigationBars());
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+        verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
     }
 
     @Test
@@ -168,10 +204,11 @@
         setOverlayViewControllerAsShowing(mOverlayViewController1);
         setupOverlayViewController2();
         when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+        reset(mWindowInsetsController);
 
         mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
 
-        verify(mCarNavigationBarController).hideBars();
+        verify(mWindowInsetsController).hide(navigationBars());
     }
 
     @Test
@@ -183,7 +220,46 @@
 
         mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
 
-        verify(mCarNavigationBarController).showBars();
+        verify(mWindowInsetsController).show(navigationBars());
+    }
+
+    @Test
+    public void showView_newHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+        reset(mWindowInsetsController);
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+        verify(mWindowInsetsController).hide(statusBars());
+    }
+
+    @Test
+    public void showView_newHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+        verify(mWindowInsetsController).show(statusBars());
+    }
+
+    @Test
+    public void showView_newHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+        setupOverlayViewController1();
+        when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+        reset(mWindowInsetsController);
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController2, mRunnable);
+
+        verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
     }
 
     @Test
@@ -216,10 +292,11 @@
         setOverlayViewControllerAsShowing(mOverlayViewController2);
         when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(true);
         when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+        reset(mWindowInsetsController);
 
         mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
 
-        verify(mCarNavigationBarController).hideBars();
+        verify(mWindowInsetsController).hide(navigationBars());
     }
 
     @Test
@@ -231,7 +308,44 @@
 
         mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
 
-        verify(mCarNavigationBarController).showBars();
+        verify(mWindowInsetsController).show(navigationBars());
+    }
+
+    @Test
+    public void showView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarsHidden() {
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+        when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+        reset(mWindowInsetsController);
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+        verify(mWindowInsetsController).hide(statusBars());
+    }
+
+    @Test
+    public void showView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarsShown() {
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+        when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+        verify(mWindowInsetsController).show(statusBars());
+    }
+
+    @Test
+    public void showView_oldHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+        when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+
+        mOverlayViewGlobalStateController.showView(mOverlayViewController1, mRunnable);
+
+        verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
     }
 
     @Test
@@ -402,10 +516,11 @@
         setupOverlayViewController2();
         setOverlayViewControllerAsShowing(mOverlayViewController2);
         when(mOverlayViewController1.shouldShowNavigationBar()).thenReturn(false);
+        reset(mWindowInsetsController);
 
         mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
 
-        verify(mCarNavigationBarController).hideBars();
+        verify(mWindowInsetsController).hide(navigationBars());
     }
 
     @Test
@@ -418,7 +533,48 @@
 
         mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
 
-        verify(mCarNavigationBarController).showBars();
+        verify(mWindowInsetsController).show(navigationBars());
+    }
+
+    @Test
+    public void hideView_newHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(false);
+        reset(mWindowInsetsController);
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+        verify(mWindowInsetsController).hide(statusBars());
+    }
+
+    @Test
+    public void hideView_newHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController1.shouldShowStatusBar()).thenReturn(true);
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+        verify(mWindowInsetsController).show(statusBars());
+    }
+
+    @Test
+    public void hideView_newHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(navigationBars());
+        when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(statusBars());
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController2, mRunnable);
+
+        verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
     }
 
     @Test
@@ -441,10 +597,11 @@
         setupOverlayViewController2();
         setOverlayViewControllerAsShowing(mOverlayViewController2);
         when(mOverlayViewController2.shouldShowNavigationBar()).thenReturn(false);
+        reset(mWindowInsetsController);
 
         mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
 
-        verify(mCarNavigationBarController).hideBars();
+        verify(mWindowInsetsController).hide(navigationBars());
     }
 
     @Test
@@ -457,7 +614,48 @@
 
         mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
 
-        verify(mCarNavigationBarController).showBars();
+        verify(mWindowInsetsController).show(navigationBars());
+    }
+
+    @Test
+    public void hideView_oldHighestZOrder_shouldShowStatusBarFalse_statusBarHidden() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(false);
+        reset(mWindowInsetsController);
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+        verify(mWindowInsetsController).hide(statusBars());
+    }
+
+    @Test
+    public void hideView_oldHighestZOrder_shouldShowStatusBarTrue_statusBarShown() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController2.shouldShowStatusBar()).thenReturn(true);
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+        verify(mWindowInsetsController).show(statusBars());
+    }
+
+    @Test
+    public void hideView_oldHighestZOrder_fitsNavBarInsets_insetsAdjusted() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+        setupOverlayViewController2();
+        setOverlayViewControllerAsShowing(mOverlayViewController2);
+        when(mOverlayViewController1.getInsetTypesToFit()).thenReturn(statusBars());
+        when(mOverlayViewController2.getInsetTypesToFit()).thenReturn(navigationBars());
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+        verify(mSystemUIOverlayWindowController).setFitInsetsTypes(navigationBars());
     }
 
     @Test
@@ -479,7 +677,27 @@
 
         mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
 
-        verify(mCarNavigationBarController).showBars();
+        verify(mWindowInsetsController).show(navigationBars());
+    }
+
+    @Test
+    public void hideView_viewControllerOnlyShown_statusBarShown() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+        verify(mWindowInsetsController).show(statusBars());
+    }
+
+    @Test
+    public void hideView_viewControllerOnlyShown_insetsAdjustedToDefault() {
+        setupOverlayViewController1();
+        setOverlayViewControllerAsShowing(mOverlayViewController1);
+
+        mOverlayViewGlobalStateController.hideView(mOverlayViewController1, mRunnable);
+
+        verify(mSystemUIOverlayWindowController).setFitInsetsTypes(statusBars());
     }
 
     @Test
@@ -615,7 +833,7 @@
 
     private void setOverlayViewControllerAsShowing(OverlayViewController overlayViewController) {
         mOverlayViewGlobalStateController.showView(overlayViewController, /* show= */ null);
-        Mockito.reset(mCarNavigationBarController, mSystemUIOverlayWindowController);
+        reset(mSystemUIOverlayWindowController);
         when(mSystemUIOverlayWindowController.getBaseLayout()).thenReturn(mBaseLayout);
     }
 }
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 16ef59f..02f4457 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -29,6 +29,7 @@
 import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
+import android.widget.AdapterView;
 import android.widget.ListView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
@@ -75,6 +76,14 @@
             mDeviceListView = findViewById(R.id.device_list);
             final DeviceDiscoveryService.DevicesAdapter adapter = getService().mDevicesAdapter;
             mDeviceListView.setAdapter(adapter);
+            mDeviceListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+                @Override
+                public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) {
+                    getService().mSelectedDevice =
+                            (DeviceFilterPair) adapterView.getItemAtPosition(pos);
+                    adapter.notifyDataSetChanged();
+                }
+            });
             adapter.registerDataSetObserver(new DataSetObserver() {
                 @Override
                 public void onChanged() {
@@ -157,4 +166,4 @@
                 new Intent().putExtra(CompanionDeviceManager.EXTRA_DEVICE, selectedDevice.device));
         finish();
     }
-}
\ No newline at end of file
+}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index 7aa997e..bcaee36 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -349,10 +349,6 @@
                         ? WIFI_ICON
                         : BLUETOOTH_ICON,
                     null, null, null);
-            textView.setOnClickListener((view) -> {
-                mSelectedDevice = device;
-                notifyDataSetChanged();
-            });
         }
 
         //TODO move to a layout file
diff --git a/packages/DynamicSystemInstallationService/tests/res/values/strings.xml b/packages/DynamicSystemInstallationService/tests/res/values/strings.xml
index fdb620b..019c0c9 100644
--- a/packages/DynamicSystemInstallationService/tests/res/values/strings.xml
+++ b/packages/DynamicSystemInstallationService/tests/res/values/strings.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <!-- testFromJsonString -->
-    <string name="blacklist_json_string" translatable="false">
+    <string name="blocklist_json_string" translatable="false">
       {
         \"entries\":[
           {
diff --git a/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java b/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java
index 82ce542..c1233eb 100644
--- a/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java
+++ b/packages/DynamicSystemInstallationService/tests/src/com/android/dynsystem/KeyRevocationListTest.java
@@ -47,32 +47,32 @@
 
     private static Context sContext;
 
-    private static String sBlacklistJsonString;
+    private static String sBlocklistJsonString;
 
     @BeforeClass
     public static void setUpClass() throws Exception {
         sContext = InstrumentationRegistry.getInstrumentation().getContext();
-        sBlacklistJsonString =
-                sContext.getString(com.android.dynsystem.tests.R.string.blacklist_json_string);
+        sBlocklistJsonString =
+                sContext.getString(com.android.dynsystem.tests.R.string.blocklist_json_string);
     }
 
     @Test
     @SmallTest
     public void testFromJsonString() throws JSONException {
-        KeyRevocationList blacklist;
-        blacklist = KeyRevocationList.fromJsonString(sBlacklistJsonString);
-        Assert.assertNotNull(blacklist);
-        Assert.assertFalse(blacklist.mEntries.isEmpty());
-        blacklist = KeyRevocationList.fromJsonString("{}");
-        Assert.assertNotNull(blacklist);
-        Assert.assertTrue(blacklist.mEntries.isEmpty());
+        KeyRevocationList blocklist;
+        blocklist = KeyRevocationList.fromJsonString(sBlocklistJsonString);
+        Assert.assertNotNull(blocklist);
+        Assert.assertFalse(blocklist.mEntries.isEmpty());
+        blocklist = KeyRevocationList.fromJsonString("{}");
+        Assert.assertNotNull(blocklist);
+        Assert.assertTrue(blocklist.mEntries.isEmpty());
     }
 
     @Test
     @SmallTest
     public void testFromUrl() throws IOException, JSONException {
         URLConnection mockConnection = mock(URLConnection.class);
-        doReturn(new ByteArrayInputStream(sBlacklistJsonString.getBytes()))
+        doReturn(new ByteArrayInputStream(sBlocklistJsonString.getBytes()))
                 .when(mockConnection).getInputStream();
         URL mockUrl = new URL(
                 "http",     // protocol
@@ -97,36 +97,36 @@
                     }
                 });
 
-        KeyRevocationList blacklist = KeyRevocationList.fromUrl(mockUrl);
-        Assert.assertNotNull(blacklist);
-        Assert.assertFalse(blacklist.mEntries.isEmpty());
+        KeyRevocationList blocklist = KeyRevocationList.fromUrl(mockUrl);
+        Assert.assertNotNull(blocklist);
+        Assert.assertFalse(blocklist.mEntries.isEmpty());
 
-        blacklist = null;
+        blocklist = null;
         try {
-            blacklist = KeyRevocationList.fromUrl(mockBadUrl);
+            blocklist = KeyRevocationList.fromUrl(mockBadUrl);
             // Up should throw, down should be unreachable
             Assert.fail("Expected IOException not thrown");
         } catch (IOException e) {
             // This is expected, do nothing
         }
-        Assert.assertNull(blacklist);
+        Assert.assertNull(blocklist);
     }
 
     @Test
     @SmallTest
     public void testIsRevoked() {
-        KeyRevocationList blacklist = new KeyRevocationList();
-        blacklist.addEntry("key1", "REVOKED", "reason for key1");
+        KeyRevocationList blocklist = new KeyRevocationList();
+        blocklist.addEntry("key1", "REVOKED", "reason for key1");
 
         KeyRevocationList.RevocationStatus revocationStatus =
-                blacklist.getRevocationStatusForKey("key1");
+                blocklist.getRevocationStatusForKey("key1");
         Assert.assertNotNull(revocationStatus);
         Assert.assertEquals(revocationStatus.mReason, "reason for key1");
 
-        revocationStatus = blacklist.getRevocationStatusForKey("key2");
+        revocationStatus = blocklist.getRevocationStatusForKey("key2");
         Assert.assertNull(revocationStatus);
 
-        Assert.assertTrue(blacklist.isRevoked("key1"));
-        Assert.assertFalse(blacklist.isRevoked("key2"));
+        Assert.assertTrue(blocklist.isRevoked("key1"));
+        Assert.assertFalse(blocklist.isRevoked("key2"));
     }
 }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 2f1191a..6914f0a 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -55,11 +55,11 @@
             "hybrid_sysui_battery_warning_flags";
 
     /**
-     * The following blacklists contain settings that should *not* be backed up and restored to
+     * The following denylists contain settings that should *not* be backed up and restored to
      * another device.  As a general rule, anything that is not user configurable should be
-     * blacklisted (and conversely, things that *are* user configurable *should* be backed up)
+     * denied (and conversely, things that *are* user configurable *should* be backed up)
      */
-    private static final Set<String> BACKUP_BLACKLISTED_SYSTEM_SETTINGS =
+    private static final Set<String> BACKUP_DENY_LIST_SYSTEM_SETTINGS =
             newHashSet(
                     Settings.System.ADVANCED_SETTINGS, // candidate for backup?
                     Settings.System.ALARM_ALERT_CACHE, // internal cache
@@ -105,7 +105,7 @@
                     Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific
                     );
 
-    private static final Set<String> BACKUP_BLACKLISTED_GLOBAL_SETTINGS =
+    private static final Set<String> BACKUP_DENY_LIST_GLOBAL_SETTINGS =
             newHashSet(
                     Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
                     Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED,
@@ -590,7 +590,7 @@
                     Settings.Global.APP_INTEGRITY_VERIFICATION_TIMEOUT,
                     Settings.Global.ADVANCED_BATTERY_USAGE_AMOUNT);
 
-    private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
+    private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
              newHashSet(
                  Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
                  Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, // Deprecated since O.
@@ -745,42 +745,42 @@
                  Settings.Secure.SUPPRESS_DOZE);
 
     @Test
-    public void systemSettingsBackedUpOrBlacklisted() {
-        checkSettingsBackedUpOrBlacklisted(
+    public void systemSettingsBackedUpOrDenied() {
+        checkSettingsBackedUpOrDenied(
                 getCandidateSettings(Settings.System.class),
                 newHashSet(SystemSettings.SETTINGS_TO_BACKUP),
-                BACKUP_BLACKLISTED_SYSTEM_SETTINGS);
+                BACKUP_DENY_LIST_SYSTEM_SETTINGS);
     }
 
     @Test
-    public void globalSettingsBackedUpOrBlacklisted() {
-        checkSettingsBackedUpOrBlacklisted(
+    public void globalSettingsBackedUpOrDenied() {
+        checkSettingsBackedUpOrDenied(
                 getCandidateSettings(Settings.Global.class),
                 newHashSet(GlobalSettings.SETTINGS_TO_BACKUP),
-                BACKUP_BLACKLISTED_GLOBAL_SETTINGS);
+                BACKUP_DENY_LIST_GLOBAL_SETTINGS);
     }
 
     @Test
     @Suppress //("b/148236308")
-    public void secureSettingsBackedUpOrBlacklisted() {
+    public void secureSettingsBackedUpOrDenied() {
         HashSet<String> keys = new HashSet<String>();
         Collections.addAll(keys, SecureSettings.SETTINGS_TO_BACKUP);
         Collections.addAll(keys, DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
-        checkSettingsBackedUpOrBlacklisted(
+        checkSettingsBackedUpOrDenied(
                 getCandidateSettings(Settings.Secure.class),
                 keys,
-                BACKUP_BLACKLISTED_SECURE_SETTINGS);
+                BACKUP_DENY_LIST_SECURE_SETTINGS);
     }
 
-    private static void checkSettingsBackedUpOrBlacklisted(
-            Set<String> settings, Set<String> settingsToBackup, Set<String> blacklist) {
+    private static void checkSettingsBackedUpOrDenied(
+            Set<String> settings, Set<String> settingsToBackup, Set<String> denylist) {
         Set<String> settingsNotBackedUp = difference(settings, settingsToBackup);
-        Set<String> settingsNotBackedUpOrBlacklisted = difference(settingsNotBackedUp, blacklist);
-        assertWithMessage("Settings not backed up or blacklisted")
-                .that(settingsNotBackedUpOrBlacklisted).isEmpty();
+        Set<String> settingsNotBackedUpOrDenied = difference(settingsNotBackedUp, denylist);
+        assertWithMessage("Settings not backed up or denied")
+                .that(settingsNotBackedUpOrDenied).isEmpty();
 
-        assertWithMessage("blacklisted settings backed up")
-                .that(intersect(settingsToBackup, blacklist)).isEmpty();
+        assertWithMessage("denied settings backed up")
+                .that(intersect(settingsToBackup, denylist)).isEmpty();
     }
 
     private static Set<String> getCandidateSettings(
diff --git a/packages/SystemUI/docs/dagger.md b/packages/SystemUI/docs/dagger.md
index bb68647..8917013 100644
--- a/packages/SystemUI/docs/dagger.md
+++ b/packages/SystemUI/docs/dagger.md
@@ -41,8 +41,6 @@
 The root component is composed of root modules, which in turn provide the global singleton 
 dependencies across all of SystemUI.
 
-- `ContextHolder` is just a wrapper that provides a context.
-
 - `SystemUIFactory` `@Provides` dependencies that need to be overridden by SystemUI
 variants (like other form factors e.g. Car). 
 
@@ -52,41 +50,8 @@
 
 ### Adding injection to a new SystemUI object
 
-Anything that depends on any `@Singleton` provider from SystemUIRootComponent
-should be declared as a `@Subcomponent` of the root component. This requires
-declaring your own interface for generating your own modules or just the
-object you need injected. The subcomponent also needs to be added to
-SystemUIRootComponent in SystemUIFactory so it can be acquired.
-
-```java
-public interface SystemUIRootComponent {
-+    @Singleton
-+    Dependency.DependencyInjector createDependency();
-}
-
-public class Dependency extends SystemUI {
-  //...
-+  @Subcomponent
-+  public interface DependencyInjector {
-+      Dependency createSystemUI();
-+  }
-}
-```
-
-For objects which extend SystemUI and require injection, you can define an
-injector that creates the injected object for you. This other class should
-be referenced in [@string/config_systemUIServiceComponents](packages/SystemUI/res/values/config.xml).
-
-```java
-public static class DependencyCreator implements Injector {
-    @Override
-    public SystemUI apply(Context context) {
-        return SystemUIFactory.getInstance().getRootComponent()
-                .createDependency()
-                .createSystemUI();
-    }
-}
-```
+SystemUI object are made injectable by adding an entry in `SystemUIBinder`. SystemUIApplication uses
+information in that file to locate and construct an instance of the requested SystemUI class.
 
 ### Adding a new injectable object
 
@@ -147,7 +112,7 @@
 
 ```java
 public interface FragmentCreator {
-+   NavigationBarFragment createNavigationBar();
+    NavigationBarFragment createNavigationBar();
 }
 ```
 
@@ -160,49 +125,17 @@
 
 ### Using injection with Views
 
-Generally, you shouldn't need to inject for a view, as the view should
-be relatively self contained and logic that requires injection should be
-moved to a higher level construct such as a Fragment or a top-level SystemUI
-component, see above for how to do injection for both of which.
+DO NOT ADD NEW VIEW INJECTION. VIEW INJECTION IS BEING ACTIVELY DEPRECATED.
 
-Still here? Yeah, ok, sysui has a lot of pre-existing views that contain a
-lot of code that could benefit from injection and will need to be migrated
-off from Dependency#get uses. Similar to how fragments are injected, the view
-needs to be added to the interface
-com.android.systemui.util.InjectionInflationController$ViewInstanceCreator.
+Needing to inject objects into your View's constructor generally implies you
+are doing more work in your presentation layer than is advisable.
+Instead, create an injected controller for you view, inject into the
+controller, and then attach the view to the controller after inflation.
 
-```java
-public interface ViewInstanceCreator {
-+   QuickStatusBarHeader createQsHeader();
-}
-```
-
-Presumably you need to inflate that view from XML (otherwise why do you
-need anything special? see earlier sections about generic injection). To obtain
-an inflater that supports injected objects, call InjectionInflationController#injectable,
-which will wrap the inflater it is passed in one that can create injected
-objects when needed.
-
-```java
-@Override
-public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
-        Bundle savedInstanceState) {
-    return mInjectionInflater.injectable(inflater).inflate(R.layout.my_layout, container, false);
-}
-```
-
-There is one other important thing to note about injecting with views. SysUI
-already has a Context in its global dagger component, so if you simply inject
-a Context, you will not get the one that the view should have with proper
-theming. Because of this, always ensure to tag views that have @Inject with
-the @Named view context.
-
-```java
-public CustomView(@Named(VIEW_CONTEXT) Context themedViewContext, AttributeSet attrs,
-        OtherCustomDependency something) {
-    //...
-}
-```
+View injection generally causes headaches while testing, as inflating a view
+(which may in turn inflate other views) implicitly causes a Dagger graph to 
+be stood up, which may or may not contain the appropriately 
+faked/mocked/stubbed objects. It is a hard to control process.
 
 ## Updating Dagger2
 
diff --git a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
index 87dd58e..213bb92 100644
--- a/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
+++ b/packages/SystemUI/res/layout/bubbles_manage_button_education.xml
@@ -14,7 +14,7 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<com.android.systemui.bubbles.BubbleManageEducationView
+<com.android.systemui.bubbles.ManageEducationView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
@@ -87,4 +87,4 @@
                 />
         </LinearLayout>
     </LinearLayout>
-</com.android.systemui.bubbles.BubbleManageEducationView>
+</com.android.systemui.bubbles.ManageEducationView>
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 4dbb92e..58f8c07 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -131,9 +131,9 @@
 
 import javax.inject.Inject;
 import javax.inject.Named;
+import javax.inject.Singleton;
 
 import dagger.Lazy;
-import dagger.Subcomponent;
 
 /**
  * Class to handle ugly dependencies throughout sysui until we determine the
@@ -150,6 +150,7 @@
  * they have no clients they should not have any registered resources like bound
  * services, registered receivers, etc.
  */
+@Singleton
 public class Dependency {
     /**
      * Key for getting a the main looper.
@@ -522,7 +523,12 @@
         mProviders.put(RecordingController.class, mRecordingController::get);
         mProviders.put(Divider.class, mDivider::get);
 
-        sDependency = this;
+        Dependency.setInstance(this);
+    }
+
+    @VisibleForTesting
+    public static void setInstance(Dependency dependency) {
+        sDependency = dependency;
     }
 
     protected final <T> T getDependency(Class<T> cls) {
@@ -549,7 +555,7 @@
     }
 
     @VisibleForTesting
-    protected <T> T createDependency(Object cls) {
+    public <T> T createDependency(Object cls) {
         Preconditions.checkArgument(cls instanceof DependencyKey<?> || cls instanceof Class<?>);
 
         @SuppressWarnings("unchecked")
@@ -638,9 +644,4 @@
             return mDisplayName;
         }
     }
-
-    @Subcomponent
-    public interface DependencyInjector {
-        void createSystemUI(Dependency dependency);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 5674fdd..1a15c0a 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui;
 
-import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Handler;
@@ -30,7 +29,6 @@
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.dagger.DaggerSystemUIRootComponent;
-import com.android.systemui.dagger.DependencyProvider;
 import com.android.systemui.dagger.SystemUIRootComponent;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.plugins.FalsingManager;
@@ -48,9 +46,6 @@
 
 import java.util.concurrent.Executor;
 
-import dagger.Module;
-import dagger.Provides;
-
 /**
  * Class factory to provide customizable SystemUI components.
  */
@@ -97,24 +92,13 @@
 
         // Every other part of our codebase currently relies on Dependency, so we
         // really need to ensure the Dependency gets initialized early on.
-
-        Dependency dependency = new Dependency();
-        mRootComponent.createDependency().createSystemUI(dependency);
+        Dependency dependency = mRootComponent.createDependency();
         dependency.start();
     }
 
-    protected void initWithRootComponent(@NonNull SystemUIRootComponent rootComponent) {
-        if (mRootComponent != null) {
-            throw new RuntimeException("Root component can be set only once.");
-        }
-
-        mRootComponent = rootComponent;
-    }
-
     protected SystemUIRootComponent buildSystemUIRootComponent(Context context) {
         return DaggerSystemUIRootComponent.builder()
-                .dependencyProvider(new DependencyProvider())
-                .contextHolder(new ContextHolder(context))
+                .context(context)
                 .build();
     }
 
@@ -168,18 +152,4 @@
                 Dependency.get(DozeParameters.class),
                 Dependency.get(BubbleController.class));
     }
-
-    @Module
-    public static class ContextHolder {
-        private Context mContext;
-
-        public ContextHolder(Context context) {
-            mContext = context;
-        }
-
-        @Provides
-        public Context provideContext() {
-            return mContext;
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
new file mode 100644
index 0000000..769a344
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/DisplayIdIndexSupplier.java
@@ -0,0 +1,81 @@
+/*
+ * 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.accessibility;
+
+import android.annotation.Nullable;
+import android.hardware.display.DisplayManager;
+import android.util.SparseArray;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Supplies the instance with given display Id. It generates a new instance if the corresponding
+ * one is not existed. It should run in single thread to avoid race conditions.
+ *
+ * @param <T> the type of results supplied by {@link #createInstance(Display)}.
+ */
+abstract class DisplayIdIndexSupplier<T> {
+
+    private final SparseArray<T> mSparseArray = new SparseArray<>();
+    private final DisplayManager mDisplayManager;
+
+    /**
+     * @param displayManager DisplayManager
+     */
+    DisplayIdIndexSupplier(DisplayManager displayManager) {
+        mDisplayManager = displayManager;
+    }
+
+    /**
+     * @param displayId the logical display Id
+     * @return {@code null} if the given display id is invalid
+     */
+    @Nullable
+    public T get(int displayId) {
+        T instance = mSparseArray.get(displayId);
+        if (instance != null) {
+            return instance;
+        }
+        final Display display = mDisplayManager.getDisplay(displayId);
+        if (display == null) {
+            return null;
+        }
+        instance = createInstance(display);
+        mSparseArray.put(displayId, instance);
+        return instance;
+    }
+
+    @NonNull
+    protected abstract T createInstance(Display display);
+
+    /**
+     * Removes the instance with given display Id.
+     *
+     * @param displayId the logical display id
+     */
+    public void remove(int displayId) {
+        mSparseArray.remove(displayId);
+    }
+
+    /**
+     * Clears all elements.
+     */
+    public void clear() {
+        mSparseArray.clear();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 398a2c9..68a0a65 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -30,6 +30,7 @@
 /**
  * Shows/hides a {@link android.widget.ImageView} on the screen and changes the values of
  * {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE} when the UI is toggled.
+ * The button UI would automatically be dismissed after displaying for a period of time.
  */
 class MagnificationModeSwitch {
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
index e73ff13..ffc70bc 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
@@ -19,31 +19,33 @@
 import android.annotation.MainThread;
 import android.content.Context;
 import android.hardware.display.DisplayManager;
-import android.util.Log;
-import android.util.SparseArray;
 import android.view.Display;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import javax.inject.Singleton;
 
 /**
- * Class to control magnification mode switch button. Shows the button UI when both full-screen
- * and window magnification mode are capable, and when the magnification scale is changed. And
- * the button UI would automatically be dismissed after displaying for a period of time.
+ * A class to control {@link MagnificationModeSwitch}. It should show the button UI with following
+ * conditions:
+ * <ol>
+ *   <li> Both full-screen and window magnification mode are capable.</li>
+ *   <li> The magnification scale is changed by a user.</li>
+ * <ol>
  */
 @Singleton
 public class ModeSwitchesController {
 
-    private static final String TAG = "ModeSwitchesController";
-
-    private final Context mContext;
-    private final DisplayManager mDisplayManager;
-
-    private final SparseArray<MagnificationModeSwitch> mDisplaysToSwitches =
-            new SparseArray<>();
+    private final SwitchSupplier mSwitchSupplier;
 
     public ModeSwitchesController(Context context) {
-        mContext = context;
-        mDisplayManager = mContext.getSystemService(DisplayManager.class);
+        mSwitchSupplier = new SwitchSupplier(context,
+                context.getSystemService(DisplayManager.class));
+    }
+
+    @VisibleForTesting
+    ModeSwitchesController(SwitchSupplier switchSupplier) {
+        mSwitchSupplier = switchSupplier;
     }
 
     /**
@@ -52,20 +54,17 @@
      *
      * @param displayId The logical display id
      * @param mode      The magnification mode
-     *
      * @see android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW
      * @see android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN
      */
     @MainThread
     void showButton(int displayId, int mode) {
-        if (mDisplaysToSwitches.get(displayId) == null) {
-            final MagnificationModeSwitch magnificationModeSwitch =
-                    createMagnificationSwitchController(displayId);
-            if (magnificationModeSwitch == null) {
-                return;
-            }
+        final MagnificationModeSwitch magnificationModeSwitch =
+                mSwitchSupplier.get(displayId);
+        if (magnificationModeSwitch == null) {
+            return;
         }
-        mDisplaysToSwitches.get(displayId).showButton(mode);
+        magnificationModeSwitch.showButton(mode);
     }
 
     /**
@@ -74,30 +73,34 @@
      * @param displayId The logical display id
      */
     void removeButton(int displayId) {
-        if (mDisplaysToSwitches.get(displayId) == null) {
+        final MagnificationModeSwitch magnificationModeSwitch =
+                mSwitchSupplier.get(displayId);
+        if (magnificationModeSwitch == null) {
             return;
         }
-        mDisplaysToSwitches.get(displayId).removeButton();
+        magnificationModeSwitch.removeButton();
     }
 
-    private MagnificationModeSwitch createMagnificationSwitchController(int displayId) {
-        if (mDisplayManager.getDisplay(displayId) == null) {
-            Log.w(TAG, "createMagnificationSwitchController displayId is invalid.");
-            return null;
+    @VisibleForTesting
+    static class SwitchSupplier extends DisplayIdIndexSupplier<MagnificationModeSwitch> {
+
+        private final Context mContext;
+
+        /**
+         * @param context        Context
+         * @param displayManager DisplayManager
+         */
+        SwitchSupplier(Context context, DisplayManager displayManager) {
+            super(displayManager);
+            mContext = context;
         }
-        final MagnificationModeSwitch
-                magnificationModeSwitch = new MagnificationModeSwitch(
-                getDisplayContext(displayId));
-        mDisplaysToSwitches.put(displayId, magnificationModeSwitch);
-        return magnificationModeSwitch;
-    }
 
-    private Context getDisplayContext(int displayId) {
-        final Display display = mDisplayManager.getDisplay(displayId);
-        final Context context = (displayId == Display.DEFAULT_DISPLAY)
-                ? mContext
-                : mContext.createDisplayContext(display);
-        return context;
+        @Override
+        protected MagnificationModeSwitch createInstance(Display display) {
+            final Context context = (display.getDisplayId() == Display.DEFAULT_DISPLAY)
+                    ? mContext
+                    : mContext.createDisplayContext(display);
+            return new MagnificationModeSwitch(context);
+        }
     }
-
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 980e4c0..361ea67 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -29,7 +29,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
@@ -39,12 +38,10 @@
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorProperties;
-import android.hardware.fingerprint.IFingerprintService;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.util.Log;
 import android.view.WindowManager;
 
@@ -247,6 +244,10 @@
         IActivityTaskManager getActivityTaskManager() {
             return ActivityTaskManager.getService();
         }
+
+        FingerprintManager getFingerprintManager(Context context) {
+            return context.getSystemService(FingerprintManager.class);
+        }
     }
 
     @Inject
@@ -273,7 +274,7 @@
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mActivityTaskManager = mInjector.getActivityTaskManager();
 
-        final FingerprintManager fpm = mContext.getSystemService(FingerprintManager.class);
+        final FingerprintManager fpm = mInjector.getFingerprintManager(mContext);
         if (fpm != null && fpm.isHardwareDetected()) {
             final List<FingerprintSensorProperties> fingerprintSensorProperties =
                     fpm.getSensorProperties();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
deleted file mode 100644
index 86244ba..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleManageEducationView.java
+++ /dev/null
@@ -1,106 +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.bubbles;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.R;
-
-/**
- * Educational view to highlight the manage button that allows a user to configure the settings
- * for the bubble. Shown only the first time a user expands a bubble.
- */
-public class BubbleManageEducationView extends LinearLayout {
-
-    private View mManageView;
-    private TextView mTitleTextView;
-    private TextView mDescTextView;
-
-    public BubbleManageEducationView(Context context) {
-        this(context, null);
-    }
-
-    public BubbleManageEducationView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public BubbleManageEducationView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public BubbleManageEducationView(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        mManageView = findViewById(R.id.manage_education_view);
-        mTitleTextView = findViewById(R.id.user_education_title);
-        mDescTextView = findViewById(R.id.user_education_description);
-
-        final TypedArray ta = mContext.obtainStyledAttributes(
-                new int[] {android.R.attr.colorAccent,
-                        android.R.attr.textColorPrimaryInverse});
-        final int bgColor = ta.getColor(0, Color.BLACK);
-        int textColor = ta.getColor(1, Color.WHITE);
-        ta.recycle();
-
-        textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true);
-        mTitleTextView.setTextColor(textColor);
-        mDescTextView.setTextColor(textColor);
-    }
-
-    /**
-     * Specifies the position for the manage view.
-     */
-    public void setManageViewPosition(int x, int y) {
-        mManageView.setTranslationX(x);
-        mManageView.setTranslationY(y);
-    }
-
-    /**
-     * @return the height of the view that shows the educational text and pointer.
-     */
-    public int getManageViewHeight() {
-        return mManageView.getHeight();
-    }
-
-    @Override
-    public void setLayoutDirection(int direction) {
-        super.setLayoutDirection(direction);
-        if (getResources().getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
-            mManageView.setBackgroundResource(R.drawable.bubble_stack_user_education_bg_rtl);
-            mTitleTextView.setGravity(Gravity.RIGHT);
-            mDescTextView.setGravity(Gravity.RIGHT);
-        } else {
-            mManageView.setBackgroundResource(R.drawable.bubble_stack_user_education_bg);
-            mTitleTextView.setGravity(Gravity.LEFT);
-            mDescTextView.setGravity(Gravity.LEFT);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index b5ad5e6..ea12c95 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -116,7 +116,7 @@
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES;
 
     /** Animation durations for bubble stack user education views. **/
-    private static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
+    static final int ANIMATE_STACK_USER_EDUCATION_DURATION = 200;
     private static final int ANIMATE_STACK_USER_EDUCATION_DURATION_SHORT = 40;
 
     /** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */
@@ -748,7 +748,7 @@
     private View mUserEducationView;
 
     private boolean mShouldShowManageEducation;
-    private BubbleManageEducationView mManageEducationView;
+    private ManageEducationView mManageEducationView;
     private boolean mAnimatingManageEducationAway;
 
     private ViewGroup mManageMenu;
@@ -1135,12 +1135,9 @@
             Log.d(TAG, "shouldShowManageEducation: " + mShouldShowManageEducation);
         }
         if (mShouldShowManageEducation) {
-            mManageEducationView = (BubbleManageEducationView)
-                    mInflater.inflate(R.layout.bubbles_manage_button_education, this,
+            mManageEducationView = (ManageEducationView)
+                    mInflater.inflate(R.layout.bubbles_manage_button_education, this /* root */,
                             false /* attachToRoot */);
-            mManageEducationView.setVisibility(GONE);
-            mManageEducationView.setElevation(mBubbleElevation);
-            mManageEducationView.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE);
             addView(mManageEducationView);
         }
     }
@@ -1753,28 +1750,8 @@
                 && mManageEducationView.getVisibility() != VISIBLE
                 && mIsExpanded
                 && mExpandedBubble.getExpandedView() != null) {
-            mManageEducationView.setAlpha(0);
-            mManageEducationView.setVisibility(VISIBLE);
-            mManageEducationView.post(() -> {
-                mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
-                final int viewHeight = mManageEducationView.getManageViewHeight();
-                final int inset = getResources().getDimensionPixelSize(
-                        R.dimen.bubbles_manage_education_top_inset);
-                mManageEducationView.bringToFront();
-                mManageEducationView.setManageViewPosition(0, mTempRect.top - viewHeight + inset);
-                mManageEducationView.animate()
-                        .setDuration(ANIMATE_STACK_USER_EDUCATION_DURATION)
-                        .setInterpolator(FAST_OUT_SLOW_IN).alpha(1);
-                mManageEducationView.findViewById(R.id.manage).setOnClickListener(view -> {
-                            mExpandedBubble.getExpandedView().findViewById(R.id.settings_button)
-                                    .performClick();
-                            maybeShowManageEducation(false);
-                        });
-                mManageEducationView.findViewById(R.id.got_it).setOnClickListener(view ->
-                        maybeShowManageEducation(false));
-                mManageEducationView.setOnClickListener(view ->
-                        maybeShowManageEducation(false));
-            });
+            mManageEducationView.show(mExpandedBubble.getExpandedView(), mTempRect,
+                    () -> maybeShowManageEducation(false) /* run on click */);
             Prefs.putBoolean(getContext(), HAS_SEEN_BUBBLES_MANAGE_EDUCATION, true);
         } else if (!show
                 && mManageEducationView.getVisibility() == VISIBLE
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
new file mode 100644
index 0000000..c58ab31
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/ManageEducationView.kt
@@ -0,0 +1,117 @@
+/*
+ * 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.bubbles
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.view.Gravity
+import android.view.View
+import android.widget.Button
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.internal.util.ContrastColorUtil
+import com.android.systemui.Interpolators
+import com.android.systemui.R
+
+/**
+ * Educational view to highlight the manage button that allows a user to configure the settings
+ * for the bubble. Shown only the first time a user expands a bubble.
+ */
+class ManageEducationView @JvmOverloads constructor(
+    context: Context?,
+    attrs: AttributeSet? = null,
+    defStyleAttr: Int = 0,
+    defStyleRes: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) {
+
+    private val manageView by lazy { findViewById<View>(R.id.manage_education_view) }
+    private val manageButton by lazy { findViewById<Button>(R.id.manage) }
+    private val gotItButton by lazy { findViewById<Button>(R.id.got_it) }
+    private val titleTextView by lazy { findViewById<TextView>(R.id.user_education_title) }
+    private val descTextView by lazy { findViewById<TextView>(R.id.user_education_description) }
+    private var isInflated = false
+
+    init {
+        this.visibility = View.GONE
+        this.elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
+        this.layoutDirection = View.LAYOUT_DIRECTION_LOCALE
+    }
+
+    override fun setLayoutDirection(direction: Int) {
+        super.setLayoutDirection(direction)
+        // setLayoutDirection runs before onFinishInflate
+        // so skip if views haven't inflated; otherwise we'll get NPEs
+        if (!isInflated) return
+        setDirection()
+    }
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        isInflated = true
+        setDirection()
+        setTextColor()
+    }
+
+    private fun setTextColor() {
+        val typedArray = mContext.obtainStyledAttributes(intArrayOf(android.R.attr.colorAccent,
+            android.R.attr.textColorPrimaryInverse))
+        val bgColor = typedArray.getColor(0 /* index */, Color.BLACK)
+        var textColor = typedArray.getColor(1 /* index */, Color.WHITE)
+        typedArray.recycle()
+        textColor = ContrastColorUtil.ensureTextContrast(textColor, bgColor, true)
+        titleTextView.setTextColor(textColor)
+        descTextView.setTextColor(textColor)
+    }
+
+    fun setDirection() {
+        manageView.setBackgroundResource(
+            if (resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL)
+                R.drawable.bubble_stack_user_education_bg_rtl
+            else R.drawable.bubble_stack_user_education_bg)
+        titleTextView.gravity = Gravity.START
+        descTextView.gravity = Gravity.START
+    }
+
+    fun show(expandedView: BubbleExpandedView, rect : Rect, hideMenu: Runnable) {
+        alpha = 0f
+        visibility = View.VISIBLE
+        post {
+            expandedView.getManageButtonBoundsOnScreen(rect)
+            with(hideMenu) {
+                manageButton
+                    .setOnClickListener {
+                        expandedView.findViewById<View>(R.id.settings_button).performClick()
+                        this.run()
+                    }
+                gotItButton.setOnClickListener { this.run() }
+                setOnClickListener { this.run() }
+            }
+            with(manageView) {
+                translationX = 0f
+                val inset = resources.getDimensionPixelSize(
+                    R.dimen.bubbles_manage_education_top_inset)
+                translationY = (rect.top - manageView.height + inset).toFloat()
+            }
+            bringToFront()
+            animate()
+                .setDuration(BubbleStackView.ANIMATE_STACK_USER_EDUCATION_DURATION.toLong())
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .alpha(1f)
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index e5cc1384..900c11f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -19,12 +19,12 @@
 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
 
 import android.content.ContentProvider;
+import android.content.Context;
 
 import com.android.systemui.BootCompleteCacheImpl;
 import com.android.systemui.Dependency;
 import com.android.systemui.InitController;
 import com.android.systemui.SystemUIAppComponentFactory;
-import com.android.systemui.SystemUIFactory;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.KeyguardSliceProvider;
@@ -37,6 +37,7 @@
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import dagger.BindsInstance;
 import dagger.Component;
 
 /**
@@ -50,13 +51,23 @@
         OneHandedModule.class,
         PipModule.class,
         SystemServicesModule.class,
-        SystemUIFactory.ContextHolder.class,
         SystemUIBinder.class,
         SystemUIModule.class,
         SystemUIDefaultModule.class})
 public interface SystemUIRootComponent {
 
     /**
+     * Builder for a SystemUIRootComponent.
+     */
+    @Component.Builder
+    interface Builder {
+        @BindsInstance
+        Builder context(Context context);
+
+        SystemUIRootComponent build();
+    }
+
+    /**
      * Provides a BootCompleteCache.
      */
     @Singleton
@@ -78,7 +89,7 @@
      * Main dependency providing module.
      */
     @Singleton
-    Dependency.DependencyInjector createDependency();
+    Dependency createDependency();
 
     /** */
     @Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
index 662831e..24ca970 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataFilter.kt
@@ -27,6 +27,7 @@
 import javax.inject.Singleton
 
 private const val TAG = "MediaDataFilter"
+private const val DEBUG = true
 
 /**
  * Filters data updates from [MediaDataCombineLatest] based on the current user ID, and handles user
@@ -98,7 +99,7 @@
         // are up to date
         mediaEntries.clear()
         keyCopy.forEach {
-            Log.d(TAG, "Removing $it after user change")
+            if (DEBUG) Log.d(TAG, "Removing $it after user change")
             listenersCopy.forEach { listener ->
                 listener.onMediaDataRemoved(it)
             }
@@ -106,7 +107,7 @@
 
         dataSource.getData().forEach { (key, data) ->
             if (lockscreenUserManager.isCurrentProfile(data.userId)) {
-                Log.d(TAG, "Re-adding $key after user change")
+                if (DEBUG) Log.d(TAG, "Re-adding $key after user change")
                 mediaEntries.put(key, data)
                 listenersCopy.forEach { listener ->
                     listener.onMediaDataLoaded(key, null, data)
@@ -119,6 +120,7 @@
      * Invoked when the user has dismissed the media carousel
      */
     fun onSwipeToDismiss() {
+        if (DEBUG) Log.d(TAG, "Media carousel swiped away")
         val mediaKeys = mediaEntries.keys.toSet()
         mediaKeys.forEach {
             mediaDataManager.setTimedOut(it, timedOut = true)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index b327773..d82150f2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -63,6 +63,7 @@
 )
 
 private const val TAG = "MediaDataManager"
+private const val DEBUG = true
 private const val DEFAULT_LUMINOSITY = 0.25f
 private const val LUMINOSITY_THRESHOLD = 0.05f
 private const val SATURATION_MULTIPLIER = 0.8f
@@ -253,7 +254,7 @@
     fun removeListener(listener: Listener) = listeners.remove(listener)
 
     /**
-     * Called whenever the player has been paused or stopped for a while.
+     * Called whenever the player has been paused or stopped for a while, or swiped from QQS.
      * This will make the player not active anymore, hiding it from QQS and Keyguard.
      * @see MediaData.active
      */
@@ -263,6 +264,7 @@
                 return
             }
             it.active = !timedOut
+            if (DEBUG) Log.d(TAG, "Updating $token timedOut: $timedOut")
             onMediaDataLoaded(token, token, it)
         }
     }
@@ -283,7 +285,9 @@
             return
         }
 
-        Log.d(TAG, "adding track for $userId from browser: $desc")
+        if (DEBUG) {
+            Log.d(TAG, "adding track for $userId from browser: $desc")
+        }
 
         // Album art
         var artworkBitmap = desc.iconBitmap
@@ -383,7 +387,7 @@
         if (actions != null) {
             for ((index, action) in actions.withIndex()) {
                 if (action.getIcon() == null) {
-                    Log.i(TAG, "No icon for action $index ${action.title}")
+                    if (DEBUG) Log.i(TAG, "No icon for action $index ${action.title}")
                     actionsToShowCollapsed.remove(index)
                     continue
                 }
@@ -427,7 +431,7 @@
             if (!TextUtils.isEmpty(uriString)) {
                 val albumArt = loadBitmapFromUri(Uri.parse(uriString))
                 if (albumArt != null) {
-                    Log.d(TAG, "loaded art from $uri")
+                    if (DEBUG) Log.d(TAG, "loaded art from $uri")
                     return albumArt
                 }
             }
@@ -514,7 +518,7 @@
         Assert.isMainThread()
         val removed = mediaEntries.remove(key)
         if (useMediaResumption && removed?.resumeAction != null) {
-            Log.d(TAG, "Not removing $key because resumable")
+            if (DEBUG) Log.d(TAG, "Not removing $key because resumable")
             // Move to resume key (aka package name) if that key doesn't already exist.
             val resumeAction = getResumeMediaAction(removed.resumeAction!!)
             val updated = removed.copy(token = null, actions = listOf(resumeAction),
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
index 0f4e6be..9239435 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedUI.java
@@ -29,7 +29,9 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
 import android.provider.Settings;
+import android.util.Log;
 
 import androidx.annotation.VisibleForTesting;
 
@@ -56,6 +58,7 @@
     private static final String TAG = "OneHandedUI";
     private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY =
             "com.android.internal.systemui.onehanded.gestural";
+    private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
 
     private final OneHandedManagerImpl mOneHandedManager;
     private final CommandQueue mCommandQueue;
@@ -136,10 +139,18 @@
             ScreenLifecycle screenLifecycle) {
         super(context);
 
+        if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+            Log.i(TAG, "Device config SUPPORT_ONE_HANDED_MODE off");
+            mCommandQueue = null;
+            mOneHandedManager = null;
+            mOverlayManager = null;
+            mSettingUtil = null;
+            mTimeoutHandler = null;
+            mScreenLifecycle = null;
+            return;
+        }
+
         mCommandQueue = commandQueue;
-        /* TODO(b/154290458) define a boolean system properties "support_one_handed_mode"
-            boolean supportOneHanded = SystemProperties.getBoolean("support_one_handed_mode");
-            if (!supportOneHanded) return; */
         mOneHandedManager = oneHandedManager;
         mSettingUtil = settingsUtil;
         mTimeoutHandler = OneHandedTimeoutHandler.get();
@@ -150,9 +161,9 @@
 
     @Override
     public void start() {
-        /* TODO(b/154290458) define a boolean system properties "support_one_handed_mode"
-            boolean supportOneHanded = SystemProperties.getBoolean("support_one_handed_mode");
-            if (!supportOneHanded) return; */
+        if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
+            return;
+        }
         mCommandQueue.addCallback(this);
         setupKeyguardUpdateMonitor();
         setupScreenObserver();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9115b48..d03082e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -809,7 +809,9 @@
 
     @Override
     public void addCallback(OverviewProxyListener listener) {
-        mConnectionCallbacks.add(listener);
+        if (!mConnectionCallbacks.contains(listener)) {
+            mConnectionCallbacks.add(listener);
+        }
         listener.onConnectionChanged(mOverviewProxy != null);
         listener.onNavBarButtonAlphaChanged(mNavBarButtonAlpha, false);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index e69319e..c535230 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -493,7 +493,7 @@
         });
         mScreenshotLayout.setOnKeyListener((v, keyCode, event) -> {
             if (keyCode == KeyEvent.KEYCODE_BACK) {
-                dismissScreenshot("back pressed", true);
+                dismissScreenshot("back pressed", false);
                 return true;
             }
             return false;
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 6f143da..a043f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -61,7 +61,7 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction()) && mScreenshot != null) {
-                mScreenshot.dismissScreenshot("close system dialogs", true);
+                mScreenshot.dismissScreenshot("close system dialogs", false);
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 8c24c54..2638d28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -154,7 +154,7 @@
                             Dependency.get(IWindowManager.class));
             navBar.setAutoHideController(autoHideController);
             navBar.restoreAppearanceAndTransientState();
-            mNavigationBars.append(displayId, navBar);
+            mNavigationBars.put(displayId, navBar);
 
             if (result != null) {
                 navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index 91cc896..ff7793d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -329,6 +329,7 @@
         // shade.
         for (i in parent.childCount - 1 downTo -1) {
             val child: View? = parent.getChildAt(i)
+
             child?.let {
                 logShadeChild(i, child)
                 // If this child is a header, update the tracked positions
@@ -342,7 +343,8 @@
                 }
             }
 
-            val row = child as? ExpandableNotificationRow
+            val row = (child as? ExpandableNotificationRow)
+                    ?.takeUnless { it.visibility == View.GONE }
 
             // Is there a section discontinuity? This usually occurs due to HUNs
             inIncomingSection = inIncomingSection || nextBucket?.let { next ->
@@ -389,7 +391,7 @@
 
             // Offset the target to account for the current position of the people header.
             peopleState?.targetPosition = peopleState?.currentPosition?.let { current ->
-                peopleState?.targetPosition?.let { target ->
+                peopleState.targetPosition?.let { target ->
                     if (current < target) target - 1 else target
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index a35aca5..9606318 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -121,6 +121,7 @@
 
     private final Context mContext;
     private final OverviewProxyService mOverviewProxyService;
+    private final SysUiState mSysUiState;
     private final Runnable mStateChangeCallback;
 
     private final PluginManager mPluginManager;
@@ -197,14 +198,22 @@
                 }
             };
 
+    private final SysUiState.SysUiStateCallback mSysUiStateCallback =
+            new SysUiState.SysUiStateCallback() {
+        @Override
+        public void onSystemUiStateChanged(int sysUiFlags) {
+            mSysUiFlags = sysUiFlags;
+        }
+    };
+
     public EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService,
-            SysUiState sysUiFlagContainer, PluginManager pluginManager,
-            Runnable stateChangeCallback) {
+            SysUiState sysUiState, PluginManager pluginManager, Runnable stateChangeCallback) {
         super(Dependency.get(BroadcastDispatcher.class));
         mContext = context;
         mDisplayId = context.getDisplayId();
         mMainExecutor = context.getMainExecutor();
         mOverviewProxyService = overviewProxyService;
+        mSysUiState = sysUiState;
         mPluginManager = pluginManager;
         mStateChangeCallback = stateChangeCallback;
         ComponentName recentsComponentName = ComponentName.unflattenFromString(
@@ -238,7 +247,6 @@
                 mContext.getMainThreadHandler(), mContext, this::onNavigationSettingsChanged);
 
         updateCurrentUserResources();
-        sysUiFlagContainer.addCallback(sysUiFlags -> mSysUiFlags = sysUiFlags);
     }
 
     public void updateCurrentUserResources() {
@@ -287,6 +295,7 @@
         mIsAttached = true;
         Dependency.get(ProtoTracer.class).add(this);
         mOverviewProxyService.addCallback(mQuickSwitchListener);
+        mSysUiState.addCallback(mSysUiStateCallback);
         updateIsEnabled();
         startTracking();
     }
@@ -298,6 +307,7 @@
         mIsAttached = false;
         Dependency.get(ProtoTracer.class).remove(this);
         mOverviewProxyService.removeCallback(mQuickSwitchListener);
+        mSysUiState.removeCallback(mSysUiStateCallback);
         updateIsEnabled();
         stopTracking();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index b7733cc..f43fa64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -120,6 +120,7 @@
 import com.android.systemui.statusbar.AutoHideUiElement;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
+import com.android.systemui.statusbar.NavigationBarController;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
@@ -131,6 +132,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
 import java.util.List;
 import java.util.Locale;
 import java.util.Optional;
@@ -354,6 +356,7 @@
             // If the button will actually become visible and the navbar is about to hide,
             // tell the statusbar to keep it around for longer
             mAutoHideController.touchAutoHide();
+            mNavigationBarView.notifyActiveTouchRegions();
         }
     };
 
@@ -550,6 +553,9 @@
             mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener(
                     mOrientationHandleGlobalLayoutListener);
         }
+        mHandler.removeCallbacks(mAutoDim);
+        mNavigationBarView = null;
+        mOrientationHandle = null;
     }
 
     @Override
@@ -1458,11 +1464,11 @@
         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
         if (navigationBarView == null) return null;
 
-        final NavigationBarFragment fragment = FragmentHostManager.get(navigationBarView)
-                .create(NavigationBarFragment.class);
         navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
+                final NavigationBarFragment fragment =
+                        FragmentHostManager.get(v).create(NavigationBarFragment.class);
                 final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
                 fragmentHost.getFragmentManager().beginTransaction()
                         .replace(R.id.navigation_bar_frame, fragment, TAG)
@@ -1472,6 +1478,8 @@
 
             @Override
             public void onViewDetachedFromWindow(View v) {
+                final FragmentHostManager fragmentHost = FragmentHostManager.get(v);
+                fragmentHost.removeTagListener(TAG, listener);
                 FragmentHostManager.removeAndDestroy(v);
                 navigationBarView.removeOnAttachStateChangeListener(this);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 7936e53..84512ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -115,12 +115,8 @@
     int mNavigationIconHints = 0;
     private int mNavBarMode;
 
-    private Rect mHomeButtonBounds = new Rect();
-    private Rect mBackButtonBounds = new Rect();
-    private Rect mRecentsButtonBounds = new Rect();
-    private Rect mRotationButtonBounds = new Rect();
     private final Region mActiveRegion = new Region();
-    private int[] mTmpPosition = new int[2];
+    private Rect mTmpBounds = new Rect();
 
     private KeyButtonDrawable mBackIcon;
     private KeyButtonDrawable mHomeDefaultIcon;
@@ -712,6 +708,7 @@
         getHomeButton().setVisibility(disableHome       ? View.INVISIBLE : View.VISIBLE);
         getRecentsButton().setVisibility(disableRecent  ? View.INVISIBLE : View.VISIBLE);
         getHomeHandle().setVisibility(disableHomeHandle ? View.INVISIBLE : View.VISIBLE);
+        notifyActiveTouchRegions();
     }
 
     @VisibleForTesting
@@ -929,42 +926,30 @@
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
 
-        mActiveRegion.setEmpty();
-        updateButtonLocation(getBackButton(), mBackButtonBounds, true);
-        updateButtonLocation(getHomeButton(), mHomeButtonBounds, false);
-        updateButtonLocation(getRecentsButton(), mRecentsButtonBounds, false);
-        updateButtonLocation(getRotateSuggestionButton(), mRotationButtonBounds, true);
-        // TODO: Handle button visibility changes
-        mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
+        notifyActiveTouchRegions();
         mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
     }
 
-    private void updateButtonLocation(ButtonDispatcher button, Rect buttonBounds,
-            boolean isActive) {
+    /**
+     * Notifies the overview service of the active touch regions.
+     */
+    public void notifyActiveTouchRegions() {
+        mActiveRegion.setEmpty();
+        updateButtonLocation(getBackButton());
+        updateButtonLocation(getHomeButton());
+        updateButtonLocation(getRecentsButton());
+        updateButtonLocation(getRotateSuggestionButton());
+        mOverviewProxyService.onActiveNavBarRegionChanges(mActiveRegion);
+    }
+
+    private void updateButtonLocation(ButtonDispatcher button) {
         View view = button.getCurrentView();
-        if (view == null) {
-            buttonBounds.setEmpty();
+        if (view == null || !button.isVisible()) {
             return;
         }
-        // Temporarily reset the translation back to origin to get the position in window
-        final float posX = view.getTranslationX();
-        final float posY = view.getTranslationY();
-        view.setTranslationX(0);
-        view.setTranslationY(0);
 
-        if (isActive) {
-            view.getLocationOnScreen(mTmpPosition);
-            buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
-                    mTmpPosition[0] + view.getMeasuredWidth(),
-                    mTmpPosition[1] + view.getMeasuredHeight());
-            mActiveRegion.op(buttonBounds, Op.UNION);
-        }
-        view.getLocationInWindow(mTmpPosition);
-        buttonBounds.set(mTmpPosition[0], mTmpPosition[1],
-                mTmpPosition[0] + view.getMeasuredWidth(),
-                mTmpPosition[1] + view.getMeasuredHeight());
-        view.setTranslationX(posX);
-        view.setTranslationY(posY);
+        view.getBoundsOnScreen(mTmpBounds);
+        mActiveRegion.op(mTmpBounds, Op.UNION);
     }
 
     private void updateOrientationViews() {
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 1f2a2c6..c5571e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -18,9 +18,6 @@
 
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
-import static android.app.StatusBarManager.DISABLE_CLOCK;
-import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.WindowType;
@@ -4174,7 +4171,6 @@
     @Override
     public void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) {
         mTopHidesStatusBar = topAppHidesStatusBar;
-        updateStatusBarIcons(topAppHidesStatusBar);
         if (!topAppHidesStatusBar && mWereIconsJustHidden) {
             // Immediately update the icon hidden state, since that should only apply if we're
             // staying fullscreen.
@@ -4184,17 +4180,6 @@
         updateHideIconsForBouncer(true /* animate */);
     }
 
-    private void updateStatusBarIcons(boolean topAppHidesStatusBar) {
-        int flags1 = StatusBarManager.DISABLE_NONE;
-        int flags2 = StatusBarManager.DISABLE2_NONE;
-        if (topAppHidesStatusBar) {
-            flags1 = DISABLE_NOTIFICATION_ICONS | DISABLE_CLOCK;
-            flags2 = DISABLE2_SYSTEM_ICONS;
-        }
-
-        mCommandQueue.disable(mDisplayId, flags1, flags2, false);
-    }
-
     protected void toggleKeyboardShortcuts(int deviceId) {
         KeyboardShortcuts.toggle(mContext, deviceId);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
index 2c8297c..dce38c1 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIRootComponent.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.tv;
 
-import android.content.Context;
-
 import com.android.systemui.dagger.DefaultComponentBinder;
 import com.android.systemui.dagger.DependencyBinder;
 import com.android.systemui.dagger.DependencyProvider;
@@ -30,7 +28,6 @@
 
 import javax.inject.Singleton;
 
-import dagger.BindsInstance;
 import dagger.Component;
 
 /**
@@ -52,9 +49,7 @@
      * Component Builder interface. This allows to bind Context instance in the component
      */
     @Component.Builder
-    interface Builder {
-        @BindsInstance Builder context(Context context);
-
+    interface Builder extends SystemUIRootComponent.Builder {
         TvSystemUIRootComponent build();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
index 7561af7..b1241b1 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java
@@ -70,6 +70,8 @@
 
         if (SystemProperties.getInt("service.adb.tcp.port", 0) == 0) {
             mDisconnectedReceiver = new UsbDisconnectedReceiver(this);
+            IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
+            mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
         }
 
         Intent intent = getIntent();
@@ -119,6 +121,7 @@
             }
             boolean connected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
             if (!connected) {
+                Log.d(TAG, "USB disconnected, notifying service");
                 notifyService(false);
                 mActivity.finish();
             }
@@ -126,29 +129,20 @@
     }
 
     @Override
-    public void onStart() {
-        super.onStart();
-        if (mDisconnectedReceiver != null) {
-            IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_STATE);
-            mBroadcastDispatcher.registerReceiver(mDisconnectedReceiver, filter);
-        }
-    }
-
-    @Override
-    protected void onStop() {
+    protected void onDestroy() {
         if (mDisconnectedReceiver != null) {
             mBroadcastDispatcher.unregisterReceiver(mDisconnectedReceiver);
         }
-        super.onStop();
-    }
-
-    @Override
-    protected void onDestroy() {
-        // If the ADB service has not yet been notified due to this dialog being closed in some
-        // other way then notify the service to deny the connection to ensure system_server sends
-        // a response to adbd.
-        if (!mServiceNotified) {
-            notifyService(false);
+        // Only notify the service if the activity is finishing; if onDestroy has been called due to
+        // a configuration change then allow the user to still authorize the connection the next
+        // time the activity is in the foreground.
+        if (isFinishing()) {
+            // If the ADB service has not yet been notified due to this dialog being closed in some
+            // other way then notify the service to deny the connection to ensure system_server
+            // sends a response to adbd.
+            if (!mServiceNotified) {
+                notifyService(false);
+            }
         }
         super.onDestroy();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
index 475ddc1..35be496 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java
@@ -46,9 +46,8 @@
     @Test
     public void testInitDependency() {
         Dependency.clearDependencies();
-        Dependency dependency = new Dependency();
-        SystemUIFactory
-                .getInstance().getRootComponent().createDependency().createSystemUI(dependency);
+        Dependency dependency =
+                SystemUIFactory.getInstance().getRootComponent().createDependency();
         dependency.start();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
index cf778504..3687b4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java
@@ -54,7 +54,10 @@
     @Before
     public void SysuiSetup() {
         SystemUIFactory.createFromConfig(mContext);
-        mDependency = new TestableDependency(mContext);
+        mDependency = new TestableDependency(
+                SystemUIFactory.getInstance().getRootComponent().createDependency());
+        Dependency.setInstance(mDependency);
+
         // TODO: Figure out another way to give reference to a SysuiTestableContext.
         mSysuiContext = (SysuiTestableContext) mContext;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index b6cc2ee..08e2784 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -73,7 +73,9 @@
     @Before
     public void SysuiSetup() throws Exception {
         SystemUIFactory.createFromConfig(mContext);
-        mDependency = new TestableDependency(mContext);
+        mDependency = new TestableDependency(
+                SystemUIFactory.getInstance().getRootComponent().createDependency());
+        Dependency.setInstance(mDependency);
         mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(mContext, mock(Looper.class),
                 mock(Executor.class), mock(DumpManager.class),
                 mock(BroadcastDispatcherLogger.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
index a7f4fa5..ee52c78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/TestableDependency.java
@@ -16,7 +16,6 @@
 
 import static org.mockito.Mockito.mock;
 
-import android.content.Context;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -26,13 +25,10 @@
 
     private final ArrayMap<Object, Object> mObjs = new ArrayMap<>();
     private final ArraySet<Object> mInstantiatedObjects = new ArraySet<>();
+    private final Dependency mParent;
 
-    public TestableDependency(Context context) {
-        SystemUIFactory.createFromConfig(context);
-        SystemUIFactory.getInstance().getRootComponent()
-                .createDependency()
-                .createSystemUI(this);
-        start();
+    public TestableDependency(Dependency parent) {
+        mParent = parent;
     }
 
     public <T> T injectMockDependency(Class<T> cls) {
@@ -53,11 +49,11 @@
     }
 
     @Override
-    protected <T> T createDependency(Object key) {
+    public <T> T createDependency(Object key) {
         if (mObjs.containsKey(key)) return (T) mObjs.get(key);
 
         mInstantiatedObjects.add(key);
-        return super.createDependency(key);
+        return mParent.createDependency(key);
     }
 
     @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
new file mode 100644
index 0000000..9cb4fb3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/DisplayIdIndexSupplierTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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.accessibility;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.hardware.display.DisplayManager;
+import android.testing.AndroidTestingRunner;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DisplayIdIndexSupplierTest extends SysuiTestCase {
+
+    private DisplayIdIndexSupplier<Object> mDisplayIdIndexSupplier;
+
+    @Before
+    public void setUp() throws Exception {
+        mDisplayIdIndexSupplier = new DisplayIdIndexSupplier(
+                mContext.getSystemService(DisplayManager.class)) {
+
+            @NonNull
+            @Override
+            protected Object createInstance(Display display) {
+                return new Object();
+            }
+        };
+    }
+
+    @Test
+    public void get_instanceIsNotNull() {
+        Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+        assertNotNull(object);
+    }
+
+    @Test
+    public void get_removeExistedObject_newObject() {
+        Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+        mDisplayIdIndexSupplier.remove(Display.DEFAULT_DISPLAY);
+
+        Object newObject = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+
+        assertNotEquals(object, newObject);
+    }
+
+    @Test
+    public void get_clearAllObjects_newObject() {
+        Object object = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+        mDisplayIdIndexSupplier.clear();
+
+        Object newObject = mDisplayIdIndexSupplier.get(Display.DEFAULT_DISPLAY);
+
+        assertNotEquals(object, newObject);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
index d6d2fcd..6948279 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
@@ -16,20 +16,13 @@
 
 package com.android.systemui.accessibility;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.content.Context;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
 import android.view.Display;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowMetrics;
 
 import androidx.test.filters.SmallTest;
 
@@ -38,45 +31,43 @@
 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)
-@TestableLooper.RunWithLooper
 /** Tests the ModeSwitchesController. */
 public class ModeSwitchesControllerTest extends SysuiTestCase {
 
-    private WindowManager mWindowManager;
+    @Mock
+    private ModeSwitchesController.SwitchSupplier mSupplier;
+    @Mock
+    private MagnificationModeSwitch mModeSwitch;
     private ModeSwitchesController mModeSwitchesController;
 
+
     @Before
     public void setUp() {
-        mWindowManager = mock(WindowManager.class);
-        Display display = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
-        when(mWindowManager.getDefaultDisplay()).thenReturn(display);
-        WindowMetrics metrics = mContext.getSystemService(WindowManager.class)
-                .getMaximumWindowMetrics();
-        when(mWindowManager.getMaximumWindowMetrics()).thenReturn(metrics);
-        mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
-        mModeSwitchesController = new ModeSwitchesController(mContext);
+        MockitoAnnotations.initMocks(this);
+        when(mSupplier.get(anyInt())).thenReturn(mModeSwitch);
+        mModeSwitchesController = new ModeSwitchesController(mSupplier);
     }
 
     @Test
     public void testShowButton() {
         mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
-        verify(mWindowManager).addView(any(), any());
+
+        verify(mModeSwitch).showButton(Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
     }
 
     @Test
     public void testRemoveButton() {
         mModeSwitchesController.showButton(Display.DEFAULT_DISPLAY,
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
-        ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class);
-        verify(mWindowManager).addView(captor.capture(), any(WindowManager.LayoutParams.class));
 
         mModeSwitchesController.removeButton(Display.DEFAULT_DISPLAY);
 
-        verify(mWindowManager).removeView(eq(captor.getValue()));
+        verify(mModeSwitch).removeButton();
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index e8a0c73..d4a94c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -45,6 +45,7 @@
 import android.hardware.biometrics.IBiometricSysuiReceiver;
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -530,6 +531,11 @@
         IActivityTaskManager getActivityTaskManager() {
             return mock(IActivityTaskManager.class);
         }
+
+        @Override
+        FingerprintManager getFingerprintManager(Context context) {
+            return mock(FingerprintManager.class);
+        }
     }
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
index 7a234a4..ffedb07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedUITest.java
@@ -20,6 +20,7 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.os.SystemProperties;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -42,6 +43,9 @@
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class OneHandedUITest extends OneHandedTestCase {
+    private static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode";
+
+    boolean mIsSupportOneHandedMode;
     CommandQueue mCommandQueue;
     KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     OneHandedUI mOneHandedUI;
@@ -58,6 +62,7 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
+        mIsSupportOneHandedMode = SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false);
         mCommandQueue = new CommandQueue(mContext);
         mScreenLifecycle = new ScreenLifecycle();
         mOneHandedUI = new OneHandedUI(mContext,
@@ -72,6 +77,10 @@
 
     @Test
     public void testStartOneHanded() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         mOneHandedUI.startOneHanded();
 
         verify(mMockOneHandedManagerImpl, times(1)).startOneHanded();
@@ -79,6 +88,10 @@
 
     @Test
     public void testStopOneHanded() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         mOneHandedUI.stopOneHanded();
 
         verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded();
@@ -86,6 +99,10 @@
 
     @Test
     public void testRegisterSettingsObserver_forEnabled() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         final String key = Settings.Secure.ONE_HANDED_MODE_ENABLED;
 
         verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any());
@@ -93,6 +110,10 @@
 
     @Test
     public void testRegisterSettingsObserver_forTimeout() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         final String key = Settings.Secure.ONE_HANDED_MODE_TIMEOUT;
 
         verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any());
@@ -100,6 +121,10 @@
 
     @Test
     public void testRegisterSettingsObserver_forTapAppExit() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         final String key = Settings.Secure.TAPS_APP_TO_EXIT;
 
         verify(mMockSettingsUtil, times(1)).registerSettingsKeyObserver(key, any(), any());
@@ -107,6 +132,10 @@
 
     @Test
     public void tesSettingsObserver_updateTapAppToExit() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.TAPS_APP_TO_EXIT, 1);
 
@@ -115,6 +144,10 @@
 
     @Test
     public void tesSettingsObserver_updateEnabled() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.ONE_HANDED_MODE_ENABLED, 1);
 
@@ -123,6 +156,10 @@
 
     @Test
     public void tesSettingsObserver_updateTimeout() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         Settings.Secure.putInt(mContext.getContentResolver(),
                 Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
                 OneHandedSettingsUtil.ONE_HANDED_TIMEOUT_MEDIUM_IN_SECONDS);
@@ -134,6 +171,10 @@
     @Ignore("Clarifying do not receive callback")
     @Test
     public void testKeyguardBouncerShowing_shouldStopOneHanded() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
 
         verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded();
@@ -141,6 +182,10 @@
 
     @Test
     public void testScreenTurningOff_shouldStopOneHanded() {
+        // Bypass test if device not support one-handed mode
+        if (!mIsSupportOneHandedMode) {
+            return;
+        }
         mScreenLifecycle.dispatchScreenTurningOff();
 
         verify(mMockOneHandedManagerImpl, times(1)).stopOneHanded();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
index 243503d..7ca2478 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManagerTest.java
@@ -380,7 +380,7 @@
 
         setupMockStack(
                 PEOPLE_HEADER,
-                ALERTING.headsUp(),
+                ALERTING,
                 PERSON,
                 ALERTING_HEADER,
                 GENTLE_HEADER,
@@ -403,9 +403,9 @@
         enablePeopleFiltering();
 
         setupMockStack(
-                PERSON.headsUp(),
+                PERSON,
                 INCOMING_HEADER,
-                ALERTING.headsUp(),
+                ALERTING,
                 PEOPLE_HEADER,
                 PERSON
         );
@@ -425,7 +425,7 @@
         enablePeopleFiltering();
 
         setupMockStack(
-                PERSON.headsUp(),
+                PERSON,
                 PEOPLE_HEADER,
                 PERSON
         );
@@ -443,8 +443,8 @@
         enablePeopleFiltering();
 
         setupMockStack(
-                ALERTING.headsUp(),
-                PERSON.headsUp()
+                ALERTING,
+                PERSON
         );
         mSectionsManager.updateSectionBoundaries();
         verifyMockStack(
@@ -461,7 +461,7 @@
 
         setupMockStack(
                 INCOMING_HEADER,
-                ALERTING.headsUp(),
+                ALERTING,
                 PEOPLE_HEADER,
                 FSN,
                 PERSON,
@@ -502,9 +502,9 @@
     public void testMediaControls_AddWhenEnterKeyguardWithHeadsUp() {
         enableMediaControls();
 
-        // GIVEN a stack that doesn't include media controls but includes HEADS_UP
+        // GIVEN a stack that doesn't include media
         setupMockStack(
-                ALERTING.headsUp(),
+                ALERTING,
                 ALERTING,
                 GENTLE_HEADER,
                 GENTLE);
@@ -584,6 +584,27 @@
         );
     }
 
+    @Test
+    public void testIgnoreGoneView() {
+        enablePeopleFiltering();
+
+        setupMockStack(
+                PERSON.gone(),
+                ALERTING,
+                GENTLE
+        );
+
+        mSectionsManager.updateSectionBoundaries();
+
+        verifyMockStack(
+                ChildType.ALERTING_HEADER,
+                ChildType.PERSON,
+                ChildType.ALERTING,
+                ChildType.GENTLE_HEADER,
+                ChildType.GENTLE
+        );
+    }
+
     private void enablePeopleFiltering() {
         when(mSectionsFeatureManager.isFilteringEnabled()).thenReturn(true);
     }
@@ -619,16 +640,16 @@
                     child = mSectionsManager.getSilentHeaderView();
                     break;
                 case FSN:
-                    child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsGone);
                     break;
                 case PERSON:
-                    child = mockNotification(BUCKET_PEOPLE, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_PEOPLE, entry.mIsGone);
                     break;
                 case ALERTING:
-                    child = mockNotification(BUCKET_ALERTING, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_ALERTING, entry.mIsGone);
                     break;
                 case GENTLE:
-                    child = mockNotification(BUCKET_SILENT, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_SILENT, entry.mIsGone);
                     break;
                 case OTHER:
                     child = mock(View.class);
@@ -643,7 +664,7 @@
         }
     }
 
-    private View mockNotification(int bucket, boolean headsUp) {
+    private View mockNotification(int bucket, boolean isGone) {
         ExpandableNotificationRow notifRow =
                 mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
         when(notifRow.getVisibility()).thenReturn(View.VISIBLE);
@@ -659,8 +680,7 @@
             return null;
         }).when(mockEntry).setBucket(anyInt());
 
-        when(notifRow.isHeadsUp()).thenReturn(headsUp);
-        when(mockEntry.isRowHeadsUp()).thenReturn(headsUp);
+        when(notifRow.getVisibility()).thenReturn(isGone ? View.GONE : View.VISIBLE);
         return notifRow;
     }
 
@@ -767,16 +787,16 @@
                     child = mSectionsManager.getSilentHeaderView();
                     break;
                 case FSN:
-                    child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_FOREGROUND_SERVICE, entry.mIsGone);
                     break;
                 case PERSON:
-                    child = mockNotification(BUCKET_PEOPLE, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_PEOPLE, entry.mIsGone);
                     break;
                 case ALERTING:
-                    child = mockNotification(BUCKET_ALERTING, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_ALERTING, entry.mIsGone);
                     break;
                 case GENTLE:
-                    child = mockNotification(BUCKET_SILENT, entry.mIsHeadsUp);
+                    child = mockNotification(BUCKET_SILENT, entry.mIsGone);
                     break;
                 case OTHER:
                     child = mock(View.class);
@@ -796,36 +816,25 @@
     private static final StackEntry ALERTING_HEADER = new StackEntry(ChildType.ALERTING_HEADER);
     private static final StackEntry GENTLE_HEADER = new StackEntry(ChildType.GENTLE_HEADER);
     private static final StackEntry FSN = new StackEntry(ChildType.FSN);
-    private static final StackEntry.Hunnable PERSON = new StackEntry.Hunnable(ChildType.PERSON);
-    private static final StackEntry.Hunnable ALERTING = new StackEntry.Hunnable(ChildType.ALERTING);
+    private static final StackEntry PERSON = new StackEntry(ChildType.PERSON);
+    private static final StackEntry ALERTING = new StackEntry(ChildType.ALERTING);
     private static final StackEntry GENTLE = new StackEntry(ChildType.GENTLE);
 
     private static class StackEntry {
         final ChildType mChildType;
-        final boolean mIsHeadsUp;
+        final boolean mIsGone;
 
         StackEntry(ChildType childType) {
             this(childType, false);
         }
 
-        StackEntry(ChildType childType, boolean isHeadsUp) {
+        StackEntry(ChildType childType, boolean isGone) {
             mChildType = childType;
-            mIsHeadsUp = isHeadsUp;
+            mIsGone = isGone;
         }
 
-        static class Hunnable extends StackEntry {
-
-            Hunnable(ChildType childType) {
-                super(childType, false);
-            }
-
-            Hunnable(ChildType childType, boolean isHeadsUp) {
-                super(childType, isHeadsUp);
-            }
-
-            public Hunnable headsUp() {
-                return new Hunnable(mChildType, true);
-            }
+        public StackEntry gone() {
+            return new StackEntry(mChildType, true);
         }
     }
 }
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 676b767..a4c6c87 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -63,7 +63,6 @@
 import android.telephony.CellSignalStrengthNr;
 import android.telephony.CellSignalStrengthTdscdma;
 import android.telephony.CellSignalStrengthWcdma;
-import android.telephony.DataFailCause;
 import android.telephony.DisconnectCause;
 import android.telephony.LocationAccessPolicy;
 import android.telephony.PhoneCapability;
@@ -1800,11 +1799,9 @@
             if (validatePhoneId(phoneId)) {
                 mPreciseDataConnectionStates.get(phoneId).put(
                         apnType,
-                        new PreciseDataConnectionState(
-                                TelephonyManager.DATA_UNKNOWN,
-                                TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                                apnType, null, null,
-                                DataFailCause.NONE, null));
+                        new PreciseDataConnectionState.Builder()
+                                .setApnTypes(apnType)
+                                .build());
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
                             PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
@@ -1986,11 +1983,10 @@
             if (validatePhoneId(phoneId)) {
                 mPreciseDataConnectionStates.get(phoneId).put(
                         apnType,
-                        new PreciseDataConnectionState(
-                                TelephonyManager.DATA_UNKNOWN,
-                                TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                                apnType, null, null,
-                                failCause, null));
+                        new PreciseDataConnectionState.Builder()
+                                .setApnTypes(apnType)
+                                .setFailCause(failCause)
+                                .build());
                 for (Record r : mRecords) {
                     if (r.matchPhoneStateListenerEvent(
                             PhoneStateListener.LISTEN_PRECISE_DATA_CONNECTION_STATE)
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index be080e5..915189c 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -81,6 +81,7 @@
 import java.util.Map;
 import java.util.Set;
 
+import static android.app.UiModeManager.DEFAULT_PRIORITY;
 import static android.app.UiModeManager.MODE_NIGHT_AUTO;
 import static android.app.UiModeManager.MODE_NIGHT_CUSTOM;
 import static android.app.UiModeManager.MODE_NIGHT_YES;
@@ -1446,6 +1447,8 @@
             pw.println("    Print this help text.");
             pw.println("  night [yes|no|auto|custom]");
             pw.println("    Set or read night mode.");
+            pw.println("  car [yes|no]");
+            pw.println("    Set or read car mode.");
             pw.println("  time [start|end] <ISO time>");
             pw.println("    Set custom start/end schedule time"
                     + " (night mode must be set to custom to apply).");
@@ -1461,6 +1464,8 @@
                 switch (cmd) {
                     case "night":
                         return handleNightMode();
+                    case "car":
+                        return handleCarMode();
                     case "time":
                         return handleCustomTime();
                     default:
@@ -1558,6 +1563,34 @@
                     return -1;
             }
         }
+
+        private int handleCarMode() throws RemoteException {
+            final PrintWriter err = getErrPrintWriter();
+            final String modeStr = getNextArg();
+            if (modeStr == null) {
+                printCurrentCarMode();
+                return 0;
+            }
+
+            if (modeStr.equals("yes")) {
+                mInterface.enableCarMode(0 /* flags */, DEFAULT_PRIORITY, "" /* package */);
+                printCurrentCarMode();
+                return 0;
+            } else if (modeStr.equals("no")) {
+                mInterface.disableCarMode(0 /* flags */);
+                printCurrentCarMode();
+                return 0;
+            } else {
+                err.println("Error: mode must be 'yes', or 'no'");
+                return -1;
+            }
+        }
+
+        private void printCurrentCarMode() throws RemoteException {
+            final PrintWriter pw = getOutPrintWriter();
+            final int currMode = mInterface.getCurrentModeType();
+            pw.println("Car mode: " + (currMode == Configuration.UI_MODE_TYPE_CAR ? "yes" : "no"));
+        }
     }
 
     public final class LocalService extends UiModeManagerInternal {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index bd51c7a..33a92e6 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -4881,12 +4881,9 @@
             if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
                 return true;
             }
-        }
-
-        final boolean hasAllowBackgroundActivityStartsToken = r.app != null
-                ? !r.app.mAllowBackgroundActivityStartsTokens.isEmpty() : false;
-        if (hasAllowBackgroundActivityStartsToken) {
-            return true;
+            if (r.app.areBackgroundActivityStartsAllowedByToken()) {
+                return true;
+            }
         }
 
         if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2b6da4c..555bf7d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -291,6 +291,7 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.EventLog;
+import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
 import android.util.PrintWriterPrinter;
@@ -310,6 +311,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IAppOpsActiveCallback;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.ProcessMap;
@@ -657,6 +659,14 @@
     int mUidChangeDispatchCount;
 
     /**
+     * Uids of apps with current active camera sessions.  Access synchronized on
+     * the IntArray instance itself, and no other locks must be acquired while that
+     * one is held.
+     */
+    @GuardedBy("mActiveCameraUids")
+    final IntArray mActiveCameraUids = new IntArray(4);
+
+    /**
      * Helper class which strips out priority and proto arguments then calls the dump function with
      * the appropriate arguments. If priority arguments are omitted, function calls the legacy
      * dump command.
@@ -2053,7 +2063,10 @@
                     }
                     if (proc != null) {
                         long startTime = SystemClock.currentThreadTimeMillis();
-                        long pss = Debug.getPss(pid, tmp, null);
+                        // skip background PSS calculation of apps that are capturing
+                        // camera imagery
+                        final boolean usingCamera = isCameraActiveForUid(proc.uid);
+                        long pss = usingCamera ? 0 : Debug.getPss(pid, tmp, null);
                         long endTime = SystemClock.currentThreadTimeMillis();
                         synchronized (ActivityManagerService.this) {
                             if (pss != 0 && proc.thread != null && proc.setProcState == procState
@@ -2066,6 +2079,7 @@
                                 ProcessList.abortNextPssTime(proc.procStateMemTracker);
                                 if (DEBUG_PSS) Slog.d(TAG_PSS, "Skipped pss collection of " + pid +
                                         ": " + (proc.thread == null ? "NO_THREAD " : "") +
+                                        (usingCamera ? "CAMERA " : "") +
                                         (proc.pid != pid ? "PID_CHANGED " : "") +
                                         " initState=" + procState + " curState=" +
                                         proc.setProcState + " " +
@@ -2155,6 +2169,14 @@
                         }
                     }
                 });
+
+        final int[] cameraOp = {AppOpsManager.OP_CAMERA};
+        mAppOpsService.startWatchingActive(cameraOp, new IAppOpsActiveCallback.Stub() {
+            @Override
+            public void opActiveChanged(int op, int uid, String packageName, boolean active) {
+                cameraActiveChanged(uid, active);
+            }
+        });
     }
 
     public void setWindowManager(WindowManagerService wm) {
@@ -4303,11 +4325,11 @@
                         intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
                         broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
                                 null, null, 0, null, null, permission.ACCESS_INSTANT_APPS, null,
-                                false, false, resolvedUserId, false);
+                                false, false, resolvedUserId, false, null);
                     } else {
                         broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
                                 null, null, 0, null, null, null, null, false, false, resolvedUserId,
-                                false);
+                                false, null);
                     }
 
                     if (observer != null) {
@@ -15691,7 +15713,7 @@
                     BroadcastQueue queue = broadcastQueueForIntent(intent);
                     BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                             null, null, -1, -1, false, null, null, OP_NONE, null, receivers,
-                            null, 0, null, null, false, true, true, -1, false,
+                            null, 0, null, null, false, true, true, -1, false, null,
                             false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
                     queue.enqueueParallelBroadcastLocked(r);
                     queue.scheduleBroadcastsLocked();
@@ -15938,7 +15960,7 @@
                 resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions,
                 appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
                 realCallingPid, userId, false /* allowBackgroundActivityStarts */,
-                null /*broadcastWhitelist*/);
+                null /* tokenNeededForBackgroundActivityStarts */, null /* broadcastWhitelist */);
     }
 
     @GuardedBy("this")
@@ -15948,6 +15970,7 @@
             Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
             boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
             int realCallingPid, int userId, boolean allowBackgroundActivityStarts,
+            @Nullable IBinder backgroundActivityStartsToken,
             @Nullable int[] broadcastWhitelist) {
         intent = new Intent(intent);
 
@@ -16038,6 +16061,8 @@
                     throw new SecurityException(msg);
                 } else {
                     allowBackgroundActivityStarts = true;
+                    // We set the token to null since if it wasn't for it we'd allow anyway here
+                    backgroundActivityStartsToken = null;
                 }
             }
         }
@@ -16506,7 +16531,8 @@
                     callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
                     resultCode, resultData, resultExtras, ordered, sticky, false, userId,
-                    allowBackgroundActivityStarts, timeoutExempt);
+                    allowBackgroundActivityStarts, backgroundActivityStartsToken,
+                    timeoutExempt);
             if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
             final boolean replaced = replacePending
                     && (queue.replaceParallelBroadcastLocked(r) != null);
@@ -16603,7 +16629,8 @@
                     callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                     resultData, resultExtras, ordered, sticky, false, userId,
-                    allowBackgroundActivityStarts, timeoutExempt);
+                    allowBackgroundActivityStarts, backgroundActivityStartsToken,
+                    timeoutExempt);
 
             if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
 
@@ -16763,7 +16790,8 @@
             int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
             IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
             String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
-            int userId, boolean allowBackgroundActivityStarts) {
+            int userId, boolean allowBackgroundActivityStarts,
+            @Nullable IBinder backgroundActivityStartsToken) {
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
 
@@ -16775,6 +16803,7 @@
                         resultTo, resultCode, resultData, resultExtras, requiredPermissions,
                         OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid,
                         realCallingPid, userId, allowBackgroundActivityStarts,
+                        backgroundActivityStartsToken,
                         null /*broadcastWhitelist*/);
             } finally {
                 Binder.restoreCallingIdentity(origId);
@@ -18266,6 +18295,27 @@
         }
     }
 
+    final void cameraActiveChanged(@UserIdInt int uid, boolean active) {
+        synchronized (mActiveCameraUids) {
+            final int curIndex = mActiveCameraUids.indexOf(uid);
+            if (active) {
+                if (curIndex < 0) {
+                    mActiveCameraUids.add(uid);
+                }
+            } else {
+                if (curIndex >= 0) {
+                    mActiveCameraUids.remove(curIndex);
+                }
+            }
+        }
+    }
+
+    final boolean isCameraActiveForUid(@UserIdInt int uid) {
+        synchronized (mActiveCameraUids) {
+            return mActiveCameraUids.indexOf(uid) >= 0;
+        }
+    }
+
     @GuardedBy("this")
     final void doStopUidLocked(int uid, final UidRecord uidRec) {
         mServices.stopInBackgroundLocked(uid);
@@ -19444,12 +19494,14 @@
                 int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
                 IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
                 String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
-                int userId, boolean allowBackgroundActivityStarts) {
+                int userId, boolean allowBackgroundActivityStarts,
+                @Nullable IBinder backgroundActivityStartsToken) {
             synchronized (ActivityManagerService.this) {
                 return ActivityManagerService.this.broadcastIntentInPackage(packageName, featureId,
                         uid, realCallingUid, realCallingPid, intent, resolvedType, resultTo,
                         resultCode, resultData, resultExtras, requiredPermission, bOptions,
-                        serialized, sticky, userId, allowBackgroundActivityStarts);
+                        serialized, sticky, userId, allowBackgroundActivityStarts,
+                        backgroundActivityStartsToken);
             }
         }
 
@@ -19471,6 +19523,7 @@
                             null /*resultExtras*/, requiredPermissions, AppOpsManager.OP_NONE,
                             null /*options*/, serialized, false /*sticky*/, callingPid, callingUid,
                             callingUid, callingPid, userId, false /*allowBackgroundStarts*/,
+                            null /*tokenNeededForBackgroundActivityStarts*/,
                             appIdWhitelist);
                 } finally {
                     Binder.restoreCallingIdentity(origId);
@@ -19482,7 +19535,8 @@
         @Override
         public ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
                 boolean fgRequired, String callingPackage, @Nullable String callingFeatureId,
-                int userId, boolean allowBackgroundActivityStarts)
+                int userId, boolean allowBackgroundActivityStarts,
+                @Nullable IBinder backgroundActivityStartsToken)
                 throws TransactionTooLargeException {
             synchronized(ActivityManagerService.this) {
                 if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 1fa62c6..12937b9 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1677,7 +1677,7 @@
         // that request - we don't want the token to be swept from under our feet...
         mHandler.removeCallbacksAndMessages(msgToken);
         // ...then add the token
-        proc.addAllowBackgroundActivityStartsToken(r);
+        proc.addAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken);
     }
 
     final void setBroadcastTimeoutLocked(long timeoutTime) {
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 40743b8..198ba34 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -92,6 +92,9 @@
     // if set to true, app's process will be temporarily allowed to start activities from background
     // for the duration of the broadcast dispatch
     final boolean allowBackgroundActivityStarts;
+    // token used to trace back the grant for activity starts, optional
+    @Nullable
+    final IBinder mBackgroundActivityStartsToken;
 
     static final int IDLE = 0;
     static final int APP_RECEIVE = 1;
@@ -240,7 +243,8 @@
             String[] _requiredPermissions, int _appOp, BroadcastOptions _options, List _receivers,
             IIntentReceiver _resultTo, int _resultCode, String _resultData, Bundle _resultExtras,
             boolean _serialized, boolean _sticky, boolean _initialSticky, int _userId,
-            boolean _allowBackgroundActivityStarts, boolean _timeoutExempt) {
+            boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken,
+            boolean timeoutExempt) {
         if (_intent == null) {
             throw new NullPointerException("Can't construct with a null intent");
         }
@@ -270,8 +274,9 @@
         userId = _userId;
         nextReceiver = 0;
         state = IDLE;
-        allowBackgroundActivityStarts = _allowBackgroundActivityStarts;
-        timeoutExempt = _timeoutExempt;
+        this.allowBackgroundActivityStarts = allowBackgroundActivityStarts;
+        mBackgroundActivityStartsToken = backgroundActivityStartsToken;
+        this.timeoutExempt = timeoutExempt;
     }
 
     /**
@@ -317,6 +322,7 @@
         manifestSkipCount = from.manifestSkipCount;
         queue = from.queue;
         allowBackgroundActivityStarts = from.allowBackgroundActivityStarts;
+        mBackgroundActivityStartsToken = from.mBackgroundActivityStartsToken;
         timeoutExempt = from.timeoutExempt;
     }
 
@@ -352,7 +358,7 @@
                 callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                 requiredPermissions, appOp, options, splitReceivers, resultTo, resultCode,
                 resultData, resultExtras, ordered, sticky, initialSticky, userId,
-                allowBackgroundActivityStarts, timeoutExempt);
+                allowBackgroundActivityStarts, mBackgroundActivityStartsToken, timeoutExempt);
 
         split.splitToken = this.splitToken;
         return split;
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 1997dbd..fbfed34 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -437,15 +437,17 @@
                     break;
                 case ActivityManager.INTENT_SENDER_BROADCAST:
                     try {
+                        final boolean allowedByToken =
+                                mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken);
+                        final IBinder bgStartsToken = (allowedByToken) ? whitelistToken : null;
+
                         // If a completion callback has been requested, require
                         // that the broadcast be delivered synchronously
                         int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName,
                                 key.featureId, uid, callingUid, callingPid, finalIntent,
                                 resolvedType, finishedReceiver, code, null, null,
                                 requiredPermission, options, (finishedReceiver != null), false,
-                                userId,
-                                mAllowBgActivityStartsForBroadcastSender.contains(whitelistToken)
-                                        || allowTrampoline);
+                                userId, allowedByToken || allowTrampoline, bgStartsToken);
                         if (sent == ActivityManager.BROADCAST_SUCCESS) {
                             sendFinish = false;
                         }
@@ -456,11 +458,14 @@
                 case ActivityManager.INTENT_SENDER_SERVICE:
                 case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
                     try {
+                        final boolean allowedByToken =
+                                mAllowBgActivityStartsForServiceSender.contains(whitelistToken);
+                        final IBinder bgStartsToken = (allowedByToken) ? whitelistToken : null;
+
                         controller.mAmInternal.startServiceInPackage(uid, finalIntent, resolvedType,
                                 key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
                                 key.packageName, key.featureId, userId,
-                                mAllowBgActivityStartsForServiceSender.contains(whitelistToken)
-                                || allowTrampoline);
+                                allowedByToken || allowTrampoline, bgStartsToken);
                     } catch (RuntimeException e) {
                         Slog.w(TAG, "Unable to send startService intent", e);
                     } catch (TransactionTooLargeException e) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index b6ad1a5..1038069 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2050,7 +2050,9 @@
             final int pid = precedence.pid;
             long now = System.currentTimeMillis();
             final long end = now + PROC_KILL_TIMEOUT;
+            final int oldPolicy = StrictMode.getThreadPolicyMask();
             try {
+                StrictMode.setThreadPolicyMask(0);
                 Process.waitForProcessDeath(pid, PROC_KILL_TIMEOUT);
                 // It's killed successfully, but we'd make sure the cleanup work is done.
                 synchronized (precedence) {
@@ -2069,9 +2071,11 @@
                     }
                 }
             } catch (Exception e) {
-                // It's still alive...
+                // It's still alive... maybe blocked at uninterruptible sleep ?
                 Slog.wtf(TAG, precedence.toString() + " refused to die, but we need to launch "
-                        + app);
+                        + app, e);
+            } finally {
+                StrictMode.setThreadPolicyMask(oldPolicy);
             }
         }
         try {
@@ -2416,7 +2420,15 @@
             ProcessList.killProcessGroup(app.uid, app.pid);
             checkSlow(startTime, "startProcess: done killing old proc");
 
-            Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+            if (!app.killed || mService.mLastMemoryLevel <= ProcessStats.ADJ_MEM_FACTOR_NORMAL
+                    || app.setProcState < ActivityManager.PROCESS_STATE_CACHED_EMPTY
+                    || app.lastCachedPss < getCachedRestoreThresholdKb()) {
+                // Throw a wtf if it's not killed, or killed but not because the system was in
+                // memory pressure + the app was in "cch-empty" and used large amount of memory
+                Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+            } else {
+                Slog.w(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+            }
             // We are not going to re-use the ProcessRecord, as we haven't dealt with the cleanup
             // routine of it yet, but we'd set it as the precedence of the new process.
             precedence = app;
@@ -2819,7 +2831,15 @@
             // We are re-adding a persistent process.  Whatevs!  Just leave it there.
             Slog.w(TAG, "Re-adding persistent process " + proc);
         } else if (old != null) {
-            Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+            if (old.killed) {
+                // The old process has been killed, we probably haven't had
+                // a chance to clean up the old record, just log a warning
+                Slog.w(TAG, "Existing proc " + old + " was killed "
+                        + (SystemClock.uptimeMillis() - old.mKillTime)
+                        + "ms ago when adding " + proc);
+            } else {
+                Slog.wtf(TAG, "Already have existing proc " + old + " when adding " + proc);
+            }
         }
         UidRecord uidRec = mActiveUids.get(proc.uid);
         if (uidRec == null) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 42e3061..cd4302b 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -25,6 +25,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.MY_PID;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ApplicationErrorReport;
 import android.app.ApplicationExitInfo;
@@ -274,9 +275,6 @@
     final ArrayMap<String, ContentProviderRecord> pubProviders = new ArrayMap<>();
     // All ContentProviderRecord process is using
     final ArrayList<ContentProviderConnection> conProviders = new ArrayList<>();
-    // A set of tokens that currently contribute to this process being temporarily allowed
-    // to start activities even if it's not in the foreground
-    final ArraySet<Binder> mAllowBackgroundActivityStartsTokens = new ArraySet<>();
     // a set of UIDs of all bound clients
     private ArraySet<Integer> mBoundClientUids = new ArraySet<>();
 
@@ -352,6 +350,8 @@
 
     boolean mReachable; // Whether or not this process is reachable from given process
 
+    long mKillTime; // The timestamp in uptime when this process was killed.
+
     void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo,
             long startTime) {
         this.startUid = startUid;
@@ -625,13 +625,6 @@
                 pw.print(prefix); pw.print("  - "); pw.println(receivers.valueAt(i));
             }
         }
-        if (mAllowBackgroundActivityStartsTokens.size() > 0) {
-            pw.print(prefix); pw.println("Background activity start tokens:");
-            for (int i = 0; i < mAllowBackgroundActivityStartsTokens.size(); i++) {
-                pw.print(prefix); pw.print("  - ");
-                pw.println(mAllowBackgroundActivityStartsTokens.valueAt(i));
-            }
-        }
     }
 
     ProcessRecord(ActivityManagerService _service, ApplicationInfo _info, String _processName,
@@ -925,6 +918,7 @@
             if (!mPersistent) {
                 killed = true;
                 killedByAm = true;
+                mKillTime = SystemClock.uptimeMillis();
             }
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
         }
@@ -1328,17 +1322,23 @@
         return mUsingWrapper;
     }
 
-    void addAllowBackgroundActivityStartsToken(Binder entity) {
+    /**
+     * Allows background activity starts using token {@param entity}. Optionally, you can provide
+     * {@param originatingToken} if you have one such originating token, this is useful for tracing
+     * back the grant in the case of the notification token.
+     */
+    void addAllowBackgroundActivityStartsToken(Binder entity, @Nullable IBinder originatingToken) {
         if (entity == null) return;
-        mAllowBackgroundActivityStartsTokens.add(entity);
-        mWindowProcessController.setAllowBackgroundActivityStarts(true);
+        mWindowProcessController.addAllowBackgroundActivityStartsToken(entity, originatingToken);
     }
 
     void removeAllowBackgroundActivityStartsToken(Binder entity) {
         if (entity == null) return;
-        mAllowBackgroundActivityStartsTokens.remove(entity);
-        mWindowProcessController.setAllowBackgroundActivityStarts(
-                !mAllowBackgroundActivityStartsTokens.isEmpty());
+        mWindowProcessController.removeAllowBackgroundActivityStartsToken(entity);
+    }
+
+    boolean areBackgroundActivityStartsAllowedByToken() {
+        return mWindowProcessController.areBackgroundActivityStartsAllowedByToken();
     }
 
     void addBoundClientUid(int clientUid) {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index db05d65..022b04d 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -575,7 +575,7 @@
                     ? _proc : null;
             if (mIsAllowedBgActivityStartsByStart
                     || mIsAllowedBgActivityStartsByBinding) {
-                _proc.addAllowBackgroundActivityStartsToken(this);
+                _proc.addAllowBackgroundActivityStartsToken(this, null);
             } else {
                 _proc.removeAllowBackgroundActivityStartsToken(this);
             }
@@ -723,7 +723,9 @@
      * {@code mIsAllowedBgActivityStartsByBinding}. If either is true, this ServiceRecord
      * should be contributing as a token in parent ProcessRecord.
      *
-     * @see com.android.server.am.ProcessRecord#mAllowBackgroundActivityStartsTokens
+     * @see com.android.server.am.ProcessRecord#addAllowBackgroundActivityStartsToken(Binder,
+     * IBinder)
+     * @see com.android.server.am.ProcessRecord#removeAllowBackgroundActivityStartsToken(Binder)
      */
     private void updateParentProcessBgActivityStartsToken() {
         if (app == null) {
@@ -732,7 +734,7 @@
         if (mIsAllowedBgActivityStartsByStart || mIsAllowedBgActivityStartsByBinding) {
             // if the token is already there it's safe to "re-add it" - we're dealing with
             // a set of Binder objects
-            app.addAllowBackgroundActivityStartsToken(this);
+            app.addAllowBackgroundActivityStartsToken(this, null);
         } else {
             app.removeAllowBackgroundActivityStartsToken(this);
         }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 9562158..1bf761f 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1891,8 +1891,7 @@
         if (callingUid != 0 && callingUid != SYSTEM_UID) {
             final boolean allow;
             final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, targetUserId);
-            if (mInjector.isCallerRecents(callingUid)
-                    && isSameProfileGroup(callingUserId, targetUserId)) {
+            if (mInjector.isCallerRecents(callingUid) && isSameProfileGroup) {
                 // If the caller is Recents and the caller has ownership of the profile group,
                 // we then allow it to access its profiles.
                 allow = true;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 30d7d54..a8a9a36 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -2290,7 +2290,7 @@
             pw.println("mHdmiControlEnabled: " + mHdmiControlEnabled);
             pw.println("mMhlInputChangeEnabled: " + mMhlInputChangeEnabled);
             pw.println("mSystemAudioActivated: " + isSystemAudioActivated());
-            pw.println("mHdmiCecVolumeControlEnabled " + mHdmiCecVolumeControlEnabled);
+            pw.println("mHdmiCecVolumeControlEnabled: " + mHdmiCecVolumeControlEnabled);
             pw.decreaseIndent();
 
             pw.println("mMhlController: ");
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index f69c823..d933c10 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -17,6 +17,9 @@
 package com.android.server.location;
 
 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.app.AppOpsManager.OP_MOCK_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -120,10 +123,16 @@
 import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
 import com.android.server.location.util.LocationAttributionHelper;
+import com.android.server.location.util.LocationPermissionsHelper;
+import com.android.server.location.util.LocationPowerSaveModeHelper;
 import com.android.server.location.util.LocationUsageLogger;
+import com.android.server.location.util.ScreenInteractiveHelper;
 import com.android.server.location.util.SettingsHelper;
 import com.android.server.location.util.SystemAppForegroundHelper;
 import com.android.server.location.util.SystemAppOpsHelper;
+import com.android.server.location.util.SystemLocationPermissionsHelper;
+import com.android.server.location.util.SystemLocationPowerSaveModeHelper;
+import com.android.server.location.util.SystemScreenInteractiveHelper;
 import com.android.server.location.util.SystemSettingsHelper;
 import com.android.server.location.util.SystemUserInfoHelper;
 import com.android.server.location.util.UserInfoHelper;
@@ -173,7 +182,7 @@
             publishBinderService(Context.LOCATION_SERVICE, mService);
 
             // client caching behavior is only enabled after seeing the first invalidate
-            invalidateLocalLocationEnabledCaches();
+            LocationManager.invalidateLocalLocationEnabledCaches();
             // disable caching for our own process
             Objects.requireNonNull(mService.mContext.getSystemService(LocationManager.class))
                     .disableLocalLocationEnabledCaches();
@@ -486,7 +495,7 @@
 
     private void onLocationModeChanged(int userId) {
         boolean enabled = mSettingsHelper.isLocationEnabled(userId);
-        invalidateLocalLocationEnabledCaches();
+        LocationManager.invalidateLocalLocationEnabledCaches();
 
         if (D) {
             Log.d(TAG, "[u" + userId + "] location enabled = " + enabled);
@@ -1232,19 +1241,20 @@
             if (!currentlyMonitoring) {
                 if (allowMonitoring) {
                     if (!highPower) {
-                        return mAppOpsHelper.startLocationMonitoring(mCallerIdentity);
+                        return mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, mCallerIdentity);
                     } else {
-                        return mAppOpsHelper.startHighPowerLocationMonitoring(mCallerIdentity);
+                        return mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION,
+                                mCallerIdentity);
                     }
                 }
             } else {
-                if (!allowMonitoring || !mAppOpsHelper.checkLocationAccess(mCallerIdentity,
+                if (!allowMonitoring || !mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
                         LocationPermissions.getPermissionLevel(mContext, mCallerIdentity.getUid(),
-                                mCallerIdentity.getPid()))) {
+                                mCallerIdentity.getPid())), mCallerIdentity)) {
                     if (!highPower) {
-                        mAppOpsHelper.stopLocationMonitoring(mCallerIdentity);
+                        mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, mCallerIdentity);
                     } else {
-                        mAppOpsHelper.stopHighPowerLocationMonitoring(mCallerIdentity);
+                        mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, mCallerIdentity);
                     }
                     return false;
                 }
@@ -1589,8 +1599,9 @@
                     continue;
                 }
 
-                if (!mAppOpsHelper.checkLocationAccess(identity,
-                        record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE)) {
+                if (!mAppOpsHelper.checkOpNoThrow(LocationPermissions.asAppOp(
+                        record.mRequest.isCoarse() ? PERMISSION_COARSE : PERMISSION_FINE),
+                        identity)) {
                     continue;
                 }
                 final boolean isBatterySaverDisablingLocation = shouldThrottleRequests
@@ -2118,7 +2129,8 @@
             }
 
             // appops check should always be right before delivery
-            if (!mAppOpsHelper.noteLocationAccess(identity, permissionLevel)) {
+            if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+                    identity)) {
                 return null;
             }
 
@@ -2179,7 +2191,8 @@
 
             if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
                 // appops check should always be right before delivery
-                if (mAppOpsHelper.noteLocationAccess(identity, permissionLevel)) {
+                if (mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+                        identity)) {
                     transport.deliverResult(lastLocation);
                 } else {
                     transport.deliverResult(null);
@@ -2329,7 +2342,7 @@
     }
 
     @Override
-    public boolean sendExtraCommand(String provider, String command, Bundle extras) {
+    public void sendExtraCommand(String provider, String command, Bundle extras) {
         LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_COARSE);
         mContext.enforceCallingOrSelfPermission(
                 permission.ACCESS_LOCATION_EXTRA_COMMANDS, null);
@@ -2350,8 +2363,6 @@
                 LocationStatsEnums.USAGE_ENDED,
                 LocationStatsEnums.API_SEND_EXTRA_COMMAND,
                 provider);
-
-        return true;
     }
 
     @Override
@@ -2553,7 +2564,8 @@
                 r.mLastFixBroadcast = location;
 
                 // appops check should always be right before delivery
-                if (!mAppOpsHelper.noteLocationAccess(receiver.mCallerIdentity, permissionLevel)) {
+                if (!mAppOpsHelper.noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel),
+                        receiver.mCallerIdentity)) {
                     continue;
                 }
 
@@ -2644,7 +2656,7 @@
         // unsafe is ok because app ops will verify the package name
         CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
                 attributionTag);
-        if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+        if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
             return;
         }
 
@@ -2664,7 +2676,7 @@
         // unsafe is ok because app ops will verify the package name
         CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
                 attributionTag);
-        if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+        if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
             return;
         }
 
@@ -2687,7 +2699,7 @@
         // unsafe is ok because app ops will verify the package name
         CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
                 attributionTag);
-        if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+        if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
             return;
         }
 
@@ -2708,7 +2720,7 @@
         // unsafe is ok because app ops will verify the package name
         CallerIdentity identity = CallerIdentity.fromBinderUnsafe(packageName,
                 attributionTag);
-        if (!mAppOpsHelper.noteMockLocationAccess(identity)) {
+        if (!mAppOpsHelper.noteOp(OP_MOCK_LOCATION, identity)) {
             return;
         }
 
@@ -2926,24 +2938,36 @@
 
         private final UserInfoHelper mUserInfoHelper;
         private final SystemAppOpsHelper mAppOpsHelper;
+        private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
         private final SystemSettingsHelper mSettingsHelper;
         private final SystemAppForegroundHelper mAppForegroundHelper;
-        private final LocationUsageLogger mLocationUsageLogger;
+        private final SystemLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
+        private final SystemScreenInteractiveHelper mScreenInteractiveHelper;
         private final LocationAttributionHelper mLocationAttributionHelper;
+        private final LocationUsageLogger mLocationUsageLogger;
+        private final LocationRequestStatistics mLocationRequestStatistics;
 
         SystemInjector(Context context, UserInfoHelper userInfoHelper) {
             mUserInfoHelper = userInfoHelper;
             mAppOpsHelper = new SystemAppOpsHelper(context);
+            mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
+                    mAppOpsHelper);
             mSettingsHelper = new SystemSettingsHelper(context);
             mAppForegroundHelper = new SystemAppForegroundHelper(context);
-            mLocationUsageLogger = new LocationUsageLogger();
+            mLocationPowerSaveModeHelper = new SystemLocationPowerSaveModeHelper(context);
+            mScreenInteractiveHelper = new SystemScreenInteractiveHelper(context);
             mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
+            mLocationUsageLogger = new LocationUsageLogger();
+            mLocationRequestStatistics = new LocationRequestStatistics();
         }
 
         void onSystemReady() {
             mAppOpsHelper.onSystemReady();
+            mLocationPermissionsHelper.onSystemReady();
             mSettingsHelper.onSystemReady();
             mAppForegroundHelper.onSystemReady();
+            mLocationPowerSaveModeHelper.onSystemReady();
+            mScreenInteractiveHelper.onSystemReady();
         }
 
         @Override
@@ -2957,6 +2981,11 @@
         }
 
         @Override
+        public LocationPermissionsHelper getLocationPermissionsHelper() {
+            return mLocationPermissionsHelper;
+        }
+
+        @Override
         public SettingsHelper getSettingsHelper() {
             return mSettingsHelper;
         }
@@ -2972,8 +3001,23 @@
         }
 
         @Override
+        public LocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
+            return mLocationPowerSaveModeHelper;
+        }
+
+        @Override
+        public ScreenInteractiveHelper getScreenInteractiveHelper() {
+            return mScreenInteractiveHelper;
+        }
+
+        @Override
         public LocationAttributionHelper getLocationAttributionHelper() {
             return mLocationAttributionHelper;
         }
+
+        @Override
+        public LocationRequestStatistics getLocationRequestStatistics() {
+            return mLocationRequestStatistics;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index f37992a..f6896b8 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -50,14 +50,10 @@
             Criteria.POWER_LOW,
             Criteria.ACCURACY_COARSE);
 
-    private volatile boolean mReportLocation;
-
     public PassiveProvider(Context context) {
         // using a direct executor is ok because this class has no locks that could deadlock
         super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context));
 
-        mReportLocation = false;
-
         setProperties(PROPERTIES);
         setAllowed(true);
     }
@@ -66,15 +62,11 @@
      * Pass a location into the passive provider.
      */
     public void updateLocation(Location location) {
-        if (mReportLocation) {
-            reportLocation(location);
-        }
+        reportLocation(location);
     }
 
     @Override
-    public void onSetRequest(ProviderRequest request) {
-        mReportLocation = request.reportLocation;
-    }
+    public void onSetRequest(ProviderRequest request) {}
 
     @Override
     protected void onExtraCommand(int uid, int pid, String command, Bundle extras) {}
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 c855a12..2d9734e 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -16,7 +16,6 @@
 
 package com.android.server.location.geofence;
 
-import static android.Manifest.permission;
 import static android.location.LocationManager.KEY_PROXIMITY_ENTERING;
 
 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
@@ -44,8 +43,8 @@
 import com.android.server.location.LocationPermissions;
 import com.android.server.location.listeners.ListenerMultiplexer;
 import com.android.server.location.listeners.PendingIntentListenerRegistration;
-import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
+import com.android.server.location.util.LocationPermissionsHelper;
 import com.android.server.location.util.LocationUsageLogger;
 import com.android.server.location.util.SettingsHelper;
 import com.android.server.location.util.UserInfoHelper;
@@ -86,7 +85,7 @@
 
         // we store these values because we don't trust the listeners not to give us dupes, not to
         // spam us, and because checking the values may be more expensive
-        private boolean mAppOpsAllowed;
+        private boolean mPermitted;
 
         private @Nullable Location mCachedLocation;
         private float mCachedLocationDistanceM;
@@ -101,7 +100,7 @@
 
             mWakeLock = Objects.requireNonNull(mContext.getSystemService(PowerManager.class))
                     .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
-                    TAG + ":" + identity.getPackageName());
+                            TAG + ":" + identity.getPackageName());
             mWakeLock.setReferenceCounted(true);
             mWakeLock.setWorkSource(identity.addToWorkSource(null));
         }
@@ -114,7 +113,8 @@
         @Override
         protected void onPendingIntentListenerRegister() {
             mGeofenceState = STATE_UNKNOWN;
-            mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
+            mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+                    getIdentity());
         }
 
         @Override
@@ -127,18 +127,32 @@
             }
         }
 
-        boolean isAppOpsAllowed() {
-            return mAppOpsAllowed;
+        boolean isPermitted() {
+            return mPermitted;
         }
 
-        boolean onAppOpsChanged(String packageName) {
+        boolean onLocationPermissionsChanged(String packageName) {
             if (getIdentity().getPackageName().equals(packageName)) {
-                boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(),
-                        PERMISSION_FINE);
-                if (appOpsAllowed != mAppOpsAllowed) {
-                    mAppOpsAllowed = appOpsAllowed;
-                    return true;
-                }
+                return onLocationPermissionsChanged();
+            }
+
+            return false;
+        }
+
+        boolean onLocationPermissionsChanged(int uid) {
+            if (getIdentity().getUid() == uid) {
+                return onLocationPermissionsChanged();
+            }
+
+            return false;
+        }
+
+        private boolean onLocationPermissionsChanged() {
+            boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+                    getIdentity());
+            if (permitted != mPermitted) {
+                mPermitted = permitted;
+                return true;
             }
 
             return false;
@@ -186,10 +200,10 @@
 
             mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
             try {
-                pendingIntent.send(mContext, 0, intent,
-                        (pI, i, rC, rD, rE) -> mWakeLock.release(),
-                        null, permission.ACCESS_FINE_LOCATION,
-                        PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
+                // send() only enforces permissions for broadcast intents, but since clients can
+                // select any kind of pending intent we do not rely on send() to enforce permissions
+                pendingIntent.send(mContext, 0, intent, (pI, i, rC, rD, rE) -> mWakeLock.release(),
+                        null, null, PendingIntentUtils.createDontSendToRestrictedAppsBundle(null));
             } catch (PendingIntent.CanceledException e) {
                 mWakeLock.release();
                 removeRegistration(new GeofenceKey(pendingIntent, getRequest()), this);
@@ -202,7 +216,7 @@
             builder.append(getIdentity());
 
             ArraySet<String> flags = new ArraySet<>(1);
-            if (!mAppOpsAllowed) {
+            if (!mPermitted) {
                 flags.add("na");
             }
             if (!flags.isEmpty()) {
@@ -224,10 +238,22 @@
     private final SettingsHelper.UserSettingChangedListener
             mLocationPackageBlacklistChangedListener =
             this::onLocationPackageBlacklistChanged;
-    private final AppOpsHelper.LocationAppOpListener mAppOpsChangedListener = this::onAppOpsChanged;
+    private final LocationPermissionsHelper.LocationPermissionsListener
+            mLocationPermissionsListener =
+            new LocationPermissionsHelper.LocationPermissionsListener() {
+                @Override
+                public void onLocationPermissionsChanged(String packageName) {
+                    GeofenceManager.this.onLocationPermissionsChanged(packageName);
+                }
+
+                @Override
+                public void onLocationPermissionsChanged(int uid) {
+                    GeofenceManager.this.onLocationPermissionsChanged(uid);
+                }
+            };
 
     protected final UserInfoHelper mUserInfoHelper;
-    protected final AppOpsHelper mAppOpsHelper;
+    protected final LocationPermissionsHelper mLocationPermissionsHelper;
     protected final SettingsHelper mSettingsHelper;
     protected final LocationUsageLogger mLocationUsageLogger;
 
@@ -241,7 +267,7 @@
         mContext = context.createAttributionContext(ATTRIBUTION_TAG);
         mUserInfoHelper = injector.getUserInfoHelper();
         mSettingsHelper = injector.getSettingsHelper();
-        mAppOpsHelper = injector.getAppOpsHelper();
+        mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
         mLocationUsageLogger = injector.getLocationUsageLogger();
     }
 
@@ -281,7 +307,7 @@
     @Override
     protected boolean isActive(GeofenceRegistration registration) {
         CallerIdentity identity = registration.getIdentity();
-        return registration.isAppOpsAllowed()
+        return registration.isPermitted()
                 && mUserInfoHelper.isCurrentUserId(identity.getUserId())
                 && mSettingsHelper.isLocationEnabled(identity.getUserId())
                 && !mSettingsHelper.isLocationPackageBlacklisted(identity.getUserId(),
@@ -294,7 +320,7 @@
         mSettingsHelper.addOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
         mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
                 mLocationPackageBlacklistChangedListener);
-        mAppOpsHelper.addListener(mAppOpsChangedListener);
+        mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
     }
 
     @Override
@@ -303,7 +329,7 @@
         mSettingsHelper.removeOnLocationEnabledChangedListener(mLocationEnabledChangedListener);
         mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
                 mLocationPackageBlacklistChangedListener);
-        mAppOpsHelper.removeListener(mAppOpsChangedListener);
+        mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
     }
 
     @Override
@@ -434,7 +460,11 @@
         updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
     }
 
-    private void onAppOpsChanged(String packageName) {
-        updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
+    private void onLocationPermissionsChanged(String packageName) {
+        updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
+    }
+
+    private void onLocationPermissionsChanged(int uid) {
+        updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
     }
 }
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 53e660a..0b7968b 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -31,8 +31,8 @@
 import com.android.server.location.listeners.BinderListenerRegistration;
 import com.android.server.location.listeners.ListenerMultiplexer;
 import com.android.server.location.util.AppForegroundHelper;
-import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
+import com.android.server.location.util.LocationPermissionsHelper;
 import com.android.server.location.util.SettingsHelper;
 import com.android.server.location.util.UserInfoHelper;
 import com.android.server.location.util.UserInfoHelper.UserListener;
@@ -65,7 +65,7 @@
         // we store these values because we don't trust the listeners not to give us dupes, not to
         // spam us, and because checking the values may be more expensive
         private boolean mForeground;
-        private boolean mAppOpsAllowed;
+        private boolean mPermitted;
 
         protected GnssListenerRegistration(@Nullable TRequest request,
                 CallerIdentity callerIdentity, TListener listener) {
@@ -84,24 +84,39 @@
             return mForeground;
         }
 
-        boolean isAppOpsAllowed() {
-            return mAppOpsAllowed;
+        boolean isPermitted() {
+            return mPermitted;
         }
 
         @Override
         protected void onBinderListenerRegister() {
-            mAppOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(), PERMISSION_FINE);
+            mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+                    getIdentity());
             mForeground = mAppForegroundHelper.isAppForeground(getIdentity().getUid());
         }
 
-        boolean onAppOpsChanged(String packageName) {
+        boolean onLocationPermissionsChanged(String packageName) {
             if (getIdentity().getPackageName().equals(packageName)) {
-                boolean appOpsAllowed = mAppOpsHelper.checkLocationAccess(getIdentity(),
-                        PERMISSION_FINE);
-                if (appOpsAllowed != mAppOpsAllowed) {
-                    mAppOpsAllowed = appOpsAllowed;
-                    return true;
-                }
+                return onLocationPermissionsChanged();
+            }
+
+            return false;
+        }
+
+        boolean onLocationPermissionsChanged(int uid) {
+            if (getIdentity().getUid() == uid) {
+                return onLocationPermissionsChanged();
+            }
+
+            return false;
+        }
+
+        private boolean onLocationPermissionsChanged() {
+            boolean permitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
+                    getIdentity());
+            if (permitted != mPermitted) {
+                mPermitted = permitted;
+                return true;
             }
 
             return false;
@@ -125,7 +140,7 @@
             if (!mForeground) {
                 flags.add("bg");
             }
-            if (!mAppOpsAllowed) {
+            if (!mPermitted) {
                 flags.add("na");
             }
             if (!flags.isEmpty()) {
@@ -141,7 +156,7 @@
 
     protected final UserInfoHelper mUserInfoHelper;
     protected final SettingsHelper mSettingsHelper;
-    protected final AppOpsHelper mAppOpsHelper;
+    protected final LocationPermissionsHelper mLocationPermissionsHelper;
     protected final AppForegroundHelper mAppForegroundHelper;
     protected final LocationManagerInternal mLocationManagerInternal;
 
@@ -154,14 +169,26 @@
     private final SettingsHelper.UserSettingChangedListener
             mLocationPackageBlacklistChangedListener =
             this::onLocationPackageBlacklistChanged;
-    private final AppOpsHelper.LocationAppOpListener mAppOpsChangedListener = this::onAppOpsChanged;
+    private final LocationPermissionsHelper.LocationPermissionsListener
+            mLocationPermissionsListener =
+            new LocationPermissionsHelper.LocationPermissionsListener() {
+                @Override
+                public void onLocationPermissionsChanged(String packageName) {
+                    GnssListenerMultiplexer.this.onLocationPermissionsChanged(packageName);
+                }
+
+                @Override
+                public void onLocationPermissionsChanged(int uid) {
+                    GnssListenerMultiplexer.this.onLocationPermissionsChanged(uid);
+                }
+            };
     private final AppForegroundHelper.AppForegroundListener mAppForegroundChangedListener =
             this::onAppForegroundChanged;
 
     protected GnssListenerMultiplexer(Injector injector) {
         mUserInfoHelper = injector.getUserInfoHelper();
         mSettingsHelper = injector.getSettingsHelper();
-        mAppOpsHelper = injector.getAppOpsHelper();
+        mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
         mAppForegroundHelper = injector.getAppForegroundHelper();
         mLocationManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(LocationManagerInternal.class));
@@ -208,7 +235,7 @@
         CallerIdentity identity = registration.getIdentity();
         // TODO: this should be checking if the gps provider is enabled, not if location is enabled,
         //  but this is the same for now.
-        return registration.isAppOpsAllowed()
+        return registration.isPermitted()
                 && (registration.isForeground() || isBackgroundRestrictionExempt(identity))
                 && mUserInfoHelper.isCurrentUserId(identity.getUserId())
                 && mSettingsHelper.isLocationEnabled(identity.getUserId())
@@ -241,7 +268,7 @@
                 mBackgroundThrottlePackageWhitelistChangedListener);
         mSettingsHelper.addOnLocationPackageBlacklistChangedListener(
                 mLocationPackageBlacklistChangedListener);
-        mAppOpsHelper.addListener(mAppOpsChangedListener);
+        mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
         mAppForegroundHelper.addListener(mAppForegroundChangedListener);
     }
 
@@ -257,7 +284,7 @@
                 mBackgroundThrottlePackageWhitelistChangedListener);
         mSettingsHelper.removeOnLocationPackageBlacklistChangedListener(
                 mLocationPackageBlacklistChangedListener);
-        mAppOpsHelper.removeListener(mAppOpsChangedListener);
+        mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
         mAppForegroundHelper.removeListener(mAppForegroundChangedListener);
     }
 
@@ -279,8 +306,12 @@
         updateRegistrations(registration -> registration.getIdentity().getUserId() == userId);
     }
 
-    private void onAppOpsChanged(String packageName) {
-        updateRegistrations(registration -> registration.onAppOpsChanged(packageName));
+    private void onLocationPermissionsChanged(String packageName) {
+        updateRegistrations(registration -> registration.onLocationPermissionsChanged(packageName));
+    }
+
+    private void onLocationPermissionsChanged(int uid) {
+        updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
     }
 
     private void onAppForegroundChanged(int uid, boolean foreground) {
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 8aaf4bf6..8004ec7 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -2008,9 +2008,7 @@
     private final class FusedLocationListener extends LocationChangeListener {
         @Override
         public void onLocationChanged(Location location) {
-            if (LocationManager.FUSED_PROVIDER.equals(location.getProvider())) {
-                injectBestLocation(location);
-            }
+            injectBestLocation(location);
         }
     }
 
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 58e725c..8e81f29 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -18,10 +18,9 @@
 
 import static android.location.LocationManager.GPS_PROVIDER;
 
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
-
 import android.Manifest;
 import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.location.GnssAntennaInfo;
 import android.location.GnssMeasurementCorrections;
@@ -47,10 +46,8 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
-import com.android.server.location.util.AppForegroundHelper;
 import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
-import com.android.server.location.util.SettingsHelper;
 
 import java.io.FileDescriptor;
 import java.util.List;
@@ -68,9 +65,7 @@
     }
 
     private final Context mContext;
-    private final SettingsHelper mSettingsHelper;
     private final AppOpsHelper mAppOpsHelper;
-    private final AppForegroundHelper mAppForegroundHelper;
     private final LocationManagerInternal mLocationManagerInternal;
 
     private final GnssLocationProvider mGnssLocationProvider;
@@ -109,9 +104,7 @@
         GnssNative.initialize();
 
         mContext = context.createAttributionContext(ATTRIBUTION_ID);
-        mSettingsHelper = injector.getSettingsHelper();
         mAppOpsHelper = injector.getAppOpsHelper();
-        mAppForegroundHelper = injector.getAppForegroundHelper();
         mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
 
         if (gnssLocationProvider == null) {
@@ -192,9 +185,10 @@
     public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName,
             String attributionTag) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
 
         CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
-        if (!mAppOpsHelper.checkLocationAccess(identity, PERMISSION_FINE)) {
+        if (!mAppOpsHelper.checkOpNoThrow(AppOpsManager.OP_FINE_LOCATION, identity)) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index 9227a17..0815d46 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -16,10 +16,10 @@
 
 package com.android.server.location.gnss;
 
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
 import static com.android.server.location.gnss.GnssManagerService.D;
 import static com.android.server.location.gnss.GnssManagerService.TAG;
 
+import android.app.AppOpsManager;
 import android.location.GnssMeasurementsEvent;
 import android.location.GnssRequest;
 import android.location.IGnssMeasurementsListener;
@@ -30,6 +30,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
+import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
 import com.android.server.location.util.LocationUsageLogger;
 import com.android.server.location.util.SettingsHelper;
@@ -47,6 +48,7 @@
         GnssListenerMultiplexer<GnssRequest, IGnssMeasurementsListener, Boolean> implements
         SettingsHelper.GlobalSettingChangedListener {
 
+    private final AppOpsHelper mAppOpsHelper;
     private final LocationUsageLogger mLogger;
     private final GnssMeasurementProviderNative mNative;
 
@@ -57,6 +59,7 @@
     @VisibleForTesting
     public GnssMeasurementsProvider(Injector injector, GnssMeasurementProviderNative aNative) {
         super(injector);
+        mAppOpsHelper = injector.getAppOpsHelper();
         mLogger = injector.getLocationUsageLogger();
         mNative = aNative;
     }
@@ -163,7 +166,8 @@
      */
     public void onMeasurementsAvailable(GnssMeasurementsEvent event) {
         deliverToListeners(registration -> {
-            if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+            if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+                    registration.getIdentity())) {
                 return listener -> listener.onGnssMeasurementsReceived(event);
             } else {
                 return null;
diff --git a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
index a07fbe4..7dcffc6 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
@@ -16,10 +16,10 @@
 
 package com.android.server.location.gnss;
 
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
 import static com.android.server.location.gnss.GnssManagerService.D;
 import static com.android.server.location.gnss.GnssManagerService.TAG;
 
+import android.app.AppOpsManager;
 import android.location.GnssNavigationMessage;
 import android.location.IGnssNavigationMessageListener;
 import android.location.util.identity.CallerIdentity;
@@ -27,6 +27,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
+import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
 
 /**
@@ -39,6 +40,7 @@
 public class GnssNavigationMessageProvider extends
         GnssListenerMultiplexer<Void, IGnssNavigationMessageListener, Void> {
 
+    private final AppOpsHelper mAppOpsHelper;
     private final GnssNavigationMessageProviderNative mNative;
 
     public GnssNavigationMessageProvider(Injector injector) {
@@ -49,6 +51,7 @@
     public GnssNavigationMessageProvider(Injector injector,
             GnssNavigationMessageProviderNative aNative) {
         super(injector);
+        mAppOpsHelper = injector.getAppOpsHelper();
         mNative = aNative;
     }
 
@@ -90,7 +93,8 @@
      */
     public void onNavigationMessageAvailable(GnssNavigationMessage event) {
         deliverToListeners(registration -> {
-            if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+            if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+                    registration.getIdentity())) {
                 return listener -> listener.onGnssNavigationMessageReceived(event);
             } else {
                 return null;
diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
index d33b058..19f7927 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -16,10 +16,10 @@
 
 package com.android.server.location.gnss;
 
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
 import static com.android.server.location.gnss.GnssManagerService.D;
 import static com.android.server.location.gnss.GnssManagerService.TAG;
 
+import android.app.AppOpsManager;
 import android.location.GnssStatus;
 import android.location.IGnssStatusListener;
 import android.location.util.identity.CallerIdentity;
@@ -27,6 +27,7 @@
 import android.stats.location.LocationStatsEnums;
 import android.util.Log;
 
+import com.android.server.location.util.AppOpsHelper;
 import com.android.server.location.util.Injector;
 import com.android.server.location.util.LocationUsageLogger;
 
@@ -35,10 +36,12 @@
  */
 public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatusListener, Void> {
 
+    private final AppOpsHelper mAppOpsHelper;
     private final LocationUsageLogger mLogger;
 
     public GnssStatusProvider(Injector injector) {
         super(injector);
+        mAppOpsHelper = injector.getAppOpsHelper();
         mLogger = injector.getLocationUsageLogger();
     }
 
@@ -113,7 +116,8 @@
      */
     public void onSvStatusChanged(GnssStatus gnssStatus) {
         deliverToListeners(registration -> {
-            if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+            if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+                    registration.getIdentity())) {
                 return listener -> listener.onSvStatusChanged(gnssStatus);
             } else {
                 return null;
@@ -126,7 +130,8 @@
      */
     public void onNmeaReceived(long timestamp, String nmea) {
         deliverToListeners(registration -> {
-            if (mAppOpsHelper.noteLocationAccess(registration.getIdentity(), PERMISSION_FINE)) {
+            if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+                    registration.getIdentity())) {
                 return listener -> listener.onNmeaReceived(timestamp, nmea);
             } else {
                 return null;
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 f5889ce..528cf8a 100644
--- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java
@@ -21,6 +21,7 @@
 import android.os.Binder;
 import android.os.Build;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.IndentingPrintWriter;
 import android.util.Pair;
 
@@ -40,8 +41,8 @@
  * A base class to multiplex client listener registrations within system server. Registrations are
  * 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)} or {@link #updateRegistration(Object, Predicate)} must be
- * invoked and return true for any registration whose active state may have changed.
+ * {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration
+ * whose active state may have changed.
  *
  * Callbacks invoked for various changes will always be ordered according to this lifecycle list:
  *
@@ -217,7 +218,6 @@
                     mRegistrations.put(key, registration);
                 }
 
-
                 if (wasEmpty) {
                     onRegister();
                 }
@@ -268,7 +268,8 @@
             try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
                  ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
 
-                for (int i = 0; i < mRegistrations.size(); i++) {
+                final int size = mRegistrations.size();
+                for (int i = 0; i < size; i++) {
                     TKey key = mRegistrations.keyAt(i);
                     if (predicate.test(key)) {
                         removeRegistration(key, mRegistrations.valueAt(i));
@@ -287,7 +288,7 @@
      * completely at some later time.
      */
     protected final void removeRegistration(@NonNull Object key,
-            @NonNull ListenerRegistration registration) {
+            @NonNull ListenerRegistration<?, ?> registration) {
         synchronized (mRegistrations) {
             int index = mRegistrations.indexOfKey(key);
             if (index < 0) {
@@ -353,7 +354,8 @@
             }
 
             ArrayList<TRegistration> actives = new ArrayList<>(mRegistrations.size());
-            for (int i = 0; i < mRegistrations.size(); i++) {
+            final int size = mRegistrations.size();
+            for (int i = 0; i < size; i++) {
                 TRegistration registration = mRegistrations.valueAt(i);
                 if (registration.isActive()) {
                     actives.add(registration);
@@ -395,7 +397,8 @@
     protected final void updateService(Predicate<TRegistration> predicate) {
         synchronized (mRegistrations) {
             boolean updateService = false;
-            for (int i = 0; i < mRegistrations.size(); i++) {
+            final int size = mRegistrations.size();
+            for (int i = 0; i < size; i++) {
                 TRegistration registration = mRegistrations.valueAt(i);
                 if (predicate.test(registration) && registration.isActive()) {
                     updateService = true;
@@ -434,7 +437,8 @@
             try (UpdateServiceBuffer ignored1 = mUpdateServiceBuffer.acquire();
                  ReentrancyGuard ignored2 = mReentrancyGuard.acquire()) {
 
-                for (int i = 0; i < mRegistrations.size(); i++) {
+                final int size = mRegistrations.size();
+                for (int i = 0; i < size; i++) {
                     TRegistration registration = mRegistrations.valueAt(i);
                     if (predicate.test(registration)) {
                         onRegistrationActiveChanged(registration);
@@ -446,33 +450,6 @@
         }
     }
 
-    /**
-     * Evaluates the predicate on the registration with the given key. 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.
-     */
-    protected final void updateRegistration(TKey key, @NonNull Predicate<TRegistration> predicate) {
-        synchronized (mRegistrations) {
-            // since updating a registration can invoke a variety of callbacks, we need to ensure
-            // those callbacks themselves do not re-enter, as this could lead to out-of-order
-            // 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()) {
-
-                TRegistration registration = mRegistrations.get(key);
-                if (registration != null && predicate.test(registration)) {
-                    onRegistrationActiveChanged(registration);
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-    }
-
     @GuardedBy("mRegistrations")
     private void onRegistrationActiveChanged(TRegistration registration) {
         if (Build.IS_DEBUGGABLE) {
@@ -511,7 +488,8 @@
         synchronized (mRegistrations) {
             long identity = Binder.clearCallingIdentity();
             try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
-                for (int i = 0; i < mRegistrations.size(); i++) {
+                final int size = mRegistrations.size();
+                for (int i = 0; i < size; i++) {
                     TRegistration registration = mRegistrations.valueAt(i);
                     if (registration.isActive()) {
                         ListenerOperation<TListener> operation = function.apply(registration);
@@ -537,7 +515,8 @@
         synchronized (mRegistrations) {
             long identity = Binder.clearCallingIdentity();
             try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) {
-                for (int i = 0; i < mRegistrations.size(); i++) {
+                final int size = mRegistrations.size();
+                for (int i = 0; i < size; i++) {
                     TRegistration registration = mRegistrations.valueAt(i);
                     if (registration.isActive()) {
                         execute(registration, operation);
@@ -571,7 +550,8 @@
                 ipw.println("listeners:");
 
                 ipw.increaseIndent();
-                for (int i = 0; i < mRegistrations.size(); i++) {
+                final int size = mRegistrations.size();
+                for (int i = 0; i < size; i++) {
                     TRegistration registration = mRegistrations.valueAt(i);
                     ipw.print(registration);
                     if (!registration.isActive()) {
@@ -612,23 +592,33 @@
      */
     private final class ReentrancyGuard implements AutoCloseable {
 
+        @GuardedBy("mRegistrations")
         private int mGuardCount;
-        private @Nullable ArrayList<Pair<Object, ListenerRegistration>> mScheduledRemovals;
+        @GuardedBy("mRegistrations")
+        private @Nullable ArraySet<Pair<Object, ListenerRegistration<?, ?>>> mScheduledRemovals;
 
         ReentrancyGuard() {
             mGuardCount = 0;
             mScheduledRemovals = null;
         }
 
+        @GuardedBy("mRegistrations")
         boolean isReentrant() {
+            if (Build.IS_DEBUGGABLE) {
+                Preconditions.checkState(Thread.holdsLock(mRegistrations));
+            }
             return mGuardCount != 0;
         }
 
-        void markForRemoval(Object key, ListenerRegistration registration) {
+        @GuardedBy("mRegistrations")
+        void markForRemoval(Object key, ListenerRegistration<?, ?> registration) {
+            if (Build.IS_DEBUGGABLE) {
+                Preconditions.checkState(Thread.holdsLock(mRegistrations));
+            }
             Preconditions.checkState(isReentrant());
 
             if (mScheduledRemovals == null) {
-                mScheduledRemovals = new ArrayList<>(mRegistrations.size());
+                mScheduledRemovals = new ArraySet<>(mRegistrations.size());
             }
             mScheduledRemovals.add(new Pair<>(key, registration));
         }
@@ -640,7 +630,7 @@
 
         @Override
         public void close() {
-            ArrayList<Pair<Object, ListenerRegistration>> scheduledRemovals = null;
+            ArraySet<Pair<Object, ListenerRegistration<?, ?>>> scheduledRemovals = null;
 
             Preconditions.checkState(mGuardCount > 0);
             if (--mGuardCount == 0) {
@@ -650,8 +640,10 @@
 
             if (scheduledRemovals != null) {
                 try (UpdateServiceBuffer ignored = mUpdateServiceBuffer.acquire()) {
-                    for (int i = 0; i < scheduledRemovals.size(); i++) {
-                        Pair<Object, ListenerRegistration> pair = scheduledRemovals.get(i);
+                    final int size = scheduledRemovals.size();
+                    for (int i = 0; i < size; i++) {
+                        Pair<Object, ListenerRegistration<?, ?>> pair = scheduledRemovals.valueAt(
+                                i);
                         removeRegistration(pair.first, pair.second);
                     }
                 }
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 e529a7d..6a815ea 100644
--- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
+++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java
@@ -61,7 +61,7 @@
      * Removes this registration. Does nothing if invoked before {@link #onRegister(Object)} or
      * after {@link #onUnregister()}. It is safe to invoke this from within either function.
      */
-    public void remove() {
+    public final void remove() {
         Object key = mKey;
         if (key != null) {
             getOwner().removeRegistration(key, this);
diff --git a/services/core/java/com/android/server/location/util/AppOpsHelper.java b/services/core/java/com/android/server/location/util/AppOpsHelper.java
index 3e42f27..1578289 100644
--- a/services/core/java/com/android/server/location/util/AppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/AppOpsHelper.java
@@ -16,15 +16,8 @@
 
 package com.android.server.location.util;
 
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
-
-import android.app.AppOpsManager;
 import android.location.util.identity.CallerIdentity;
 
-import com.android.server.location.LocationPermissions;
-import com.android.server.location.LocationPermissions.PermissionLevel;
-
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
@@ -70,84 +63,27 @@
     }
 
     /**
-     * Checks if the given identity may have locations delivered without noting that a location is
-     * being delivered. This is a looser guarantee than
-     * {@link #noteLocationAccess(CallerIdentity, int)}, and this function does not validate package
-     * arguments and so should not be used with unvalidated arguments or before actually delivering
-     * locations.
-     *
-     * @see AppOpsManager#checkOpNoThrow(int, int, String)
+     * Starts the given appop.
      */
-    public final boolean checkLocationAccess(CallerIdentity callerIdentity,
-            @PermissionLevel int permissionLevel) {
-        if (permissionLevel == LocationPermissions.PERMISSION_NONE) {
-            return false;
-        }
-
-        return checkOpNoThrow(LocationPermissions.asAppOp(permissionLevel), callerIdentity);
-    }
+    public abstract boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity);
 
     /**
-     * Notes location access to the given identity, ie, location delivery. This method should be
-     * called right before a location is delivered, and if it returns false, the location should not
-     * be delivered.
+     * Finishes the given appop.
      */
-    public final boolean noteLocationAccess(CallerIdentity identity,
-            @PermissionLevel int permissionLevel) {
-        if (permissionLevel == LocationPermissions.PERMISSION_NONE) {
-            return false;
-        }
-
-        return noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), identity);
-    }
+    public abstract void finishOp(int appOp, CallerIdentity callerIdentity);
 
     /**
-     * Notifies app ops that the given identity is using location at normal/low power levels. If
-     * this function returns false, do not later call
-     * {@link #stopLocationMonitoring(CallerIdentity)}.
+     * Checks the given appop.
      */
-    public final boolean startLocationMonitoring(CallerIdentity identity) {
-        return startOpNoThrow(OP_MONITOR_LOCATION, identity);
-    }
+    public abstract boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity);
 
     /**
-     * Notifies app ops that the given identity is no longer using location at normal/low power
-     * levels.
+     * Notes the given appop (and may throw a security exception).
      */
-    public final void stopLocationMonitoring(CallerIdentity identity) {
-        finishOp(OP_MONITOR_LOCATION, identity);
-    }
+    public abstract boolean noteOp(int appOp, CallerIdentity callerIdentity);
 
     /**
-     * Notifies app ops that the given identity is using location at high levels. If this function
-     * returns false, do not later call {@link #stopLocationMonitoring(CallerIdentity)}.
+     * Notes the given appop.
      */
-    public final boolean startHighPowerLocationMonitoring(CallerIdentity identity) {
-        return startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity);
-    }
-
-    /**
-     * Notifies app ops that the given identity is no longer using location at high power levels.
-     */
-    public final void stopHighPowerLocationMonitoring(CallerIdentity identity) {
-        finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
-    }
-
-    /**
-     * Notes access to any mock location APIs. If this call returns false, access to the APIs should
-     * silently fail.
-     */
-    public final boolean noteMockLocationAccess(CallerIdentity callerIdentity) {
-        return noteOp(AppOpsManager.OP_MOCK_LOCATION, callerIdentity);
-    }
-
-    protected abstract boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity);
-
-    protected abstract void finishOp(int appOp, CallerIdentity callerIdentity);
-
-    protected abstract boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity);
-
-    protected abstract boolean noteOp(int appOp, CallerIdentity callerIdentity);
-
-    protected abstract boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity);
+    public abstract boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity);
 }
diff --git a/services/core/java/com/android/server/location/util/Injector.java b/services/core/java/com/android/server/location/util/Injector.java
index e16df5d..379b303 100644
--- a/services/core/java/com/android/server/location/util/Injector.java
+++ b/services/core/java/com/android/server/location/util/Injector.java
@@ -17,6 +17,7 @@
 package com.android.server.location.util;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.location.LocationRequestStatistics;
 
 /**
  * Injects various location dependencies so that they may be controlled by tests.
@@ -30,15 +31,27 @@
     /** Returns an AppOpsHelper. */
     AppOpsHelper getAppOpsHelper();
 
+    /** Returns a LocationPermissionsHelper. */
+    LocationPermissionsHelper getLocationPermissionsHelper();
+
     /** Returns a SettingsHelper. */
     SettingsHelper getSettingsHelper();
 
     /** Returns an AppForegroundHelper. */
     AppForegroundHelper getAppForegroundHelper();
 
-    /** Returns a LocationUsageLogger. */
-    LocationUsageLogger getLocationUsageLogger();
+    /** Returns a LocationPowerSaveModeHelper. */
+    LocationPowerSaveModeHelper getLocationPowerSaveModeHelper();
+
+    /** Returns a ScreenInteractiveHelper. */
+    ScreenInteractiveHelper getScreenInteractiveHelper();
 
     /** Returns a LocationAttributionHelper. */
     LocationAttributionHelper getLocationAttributionHelper();
+
+    /** Returns a LocationUsageLogger. */
+    LocationUsageLogger getLocationUsageLogger();
+
+    /** Returns a LocationRequestStatistics. */
+    LocationRequestStatistics getLocationRequestStatistics();
 }
diff --git a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
index 8fe0941..bc3ac0f 100644
--- a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
+++ b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java
@@ -16,9 +16,16 @@
 
 package com.android.server.location.util;
 
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
+
 import android.location.util.identity.CallerIdentity;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -83,7 +90,7 @@
                 i -> new ArraySet<>());
         boolean empty = keySet.isEmpty();
         if (keySet.add(new ProviderListener(provider, key)) && empty) {
-            if (!mAppOpsHelper.startLocationMonitoring(identity)) {
+            if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) {
                 mAttributions.remove(identity);
             }
         }
@@ -99,7 +106,7 @@
         if (keySet != null && keySet.remove(new ProviderListener(provider, key))
                 && keySet.isEmpty()) {
             mAttributions.remove(identity);
-            mAppOpsHelper.stopLocationMonitoring(identity);
+            mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity);
         }
     }
 
@@ -113,14 +120,18 @@
                 i -> new ArraySet<>());
         boolean empty = keySet.isEmpty();
         if (keySet.add(new ProviderListener(provider, key)) && empty) {
-            if (!mAppOpsHelper.startHighPowerLocationMonitoring(identity)) {
+            if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) {
+                if (D) {
+                    Log.v(TAG, "starting high power location attribution for " + identity);
+                }
+            } else {
                 mHighPowerAttributions.remove(identity);
             }
         }
     }
 
     /**
-     * Report high power  location usage has stopped for the given caller on the given provider,
+     * Report high power location usage has stopped for the given caller on the given provider,
      * with a unique key.
      */
     public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String provider,
@@ -128,8 +139,11 @@
         Set<ProviderListener> keySet = mHighPowerAttributions.get(identity);
         if (keySet != null && keySet.remove(new ProviderListener(provider, key))
                 && keySet.isEmpty()) {
+            if (D) {
+                Log.v(TAG, "stopping high power location attribution for " + identity);
+            }
             mHighPowerAttributions.remove(identity);
-            mAppOpsHelper.stopHighPowerLocationMonitoring(identity);
+            mAppOpsHelper.finishOp(OP_MONITOR_HIGH_POWER_LOCATION, identity);
         }
     }
 }
diff --git a/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java b/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java
new file mode 100644
index 0000000..daf5679
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/LocationPermissionsHelper.java
@@ -0,0 +1,107 @@
+/*
+ * 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.util;
+
+import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
+
+import android.location.util.identity.CallerIdentity;
+
+import com.android.server.location.LocationPermissions;
+import com.android.server.location.LocationPermissions.PermissionLevel;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides helpers and listeners for appops.
+ */
+public abstract class LocationPermissionsHelper {
+
+    /**
+     * Listener for current user changes.
+     */
+    public interface LocationPermissionsListener {
+
+        /**
+         * Called when something has changed about location permissions for the given package.
+         */
+        void onLocationPermissionsChanged(String packageName);
+
+        /**
+         * Called when something has changed about location permissions for the given uid.
+         */
+        void onLocationPermissionsChanged(int uid);
+    }
+
+    private final CopyOnWriteArrayList<LocationPermissionsListener> mListeners;
+    private final AppOpsHelper mAppOps;
+
+    public LocationPermissionsHelper(AppOpsHelper appOps) {
+        mListeners = new CopyOnWriteArrayList<>();
+        mAppOps = appOps;
+
+        mAppOps.addListener(this::onAppOpsChanged);
+    }
+
+    protected final void notifyLocationPermissionsChanged(String packageName) {
+        for (LocationPermissionsListener listener : mListeners) {
+            listener.onLocationPermissionsChanged(packageName);
+        }
+    }
+
+    protected final void notifyLocationPermissionsChanged(int uid) {
+        for (LocationPermissionsListener listener : mListeners) {
+            listener.onLocationPermissionsChanged(uid);
+        }
+    }
+
+    private void onAppOpsChanged(String packageName) {
+        notifyLocationPermissionsChanged(packageName);
+    }
+
+    /**
+     * Adds a listener for location permissions events. Callbacks occur on an unspecified thread.
+     */
+    public final void addListener(LocationPermissionsListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener for location permissions events.
+     */
+    public final void removeListener(LocationPermissionsListener listener) {
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Returns true if the given identity may access location at the given permissions level, taking
+     * into account both permissions and appops.
+     */
+    public final boolean hasLocationPermissions(@PermissionLevel int permissionLevel,
+            CallerIdentity identity) {
+        if (permissionLevel == PERMISSION_NONE) {
+            return false;
+        }
+
+        if (!hasPermission(LocationPermissions.asPermission(permissionLevel), identity)) {
+            return false;
+        }
+
+        return mAppOps.checkOpNoThrow(permissionLevel, identity);
+    }
+
+    protected abstract boolean hasPermission(String permission, CallerIdentity callerIdentity);
+}
diff --git a/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
new file mode 100644
index 0000000..a9a8c50
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/LocationPowerSaveModeHelper.java
@@ -0,0 +1,71 @@
+/*
+ * 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.util;
+
+import android.os.PowerManager.LocationPowerSaveMode;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for location power save mode.
+ */
+public abstract class LocationPowerSaveModeHelper {
+
+    /**
+     * Listener for location power save mode changes.
+     */
+    public interface LocationPowerSaveModeChangedListener {
+        /**
+         * Called when the location power save mode changes.
+         */
+        void onLocationPowerSaveModeChanged(@LocationPowerSaveMode int locationPowerSaveMode);
+    }
+
+    private final CopyOnWriteArrayList<LocationPowerSaveModeChangedListener> mListeners;
+
+    public LocationPowerSaveModeHelper() {
+        mListeners = new CopyOnWriteArrayList<>();
+    }
+
+    /**
+     * Add a listener for changes to location power save mode. Callbacks occur on an unspecified
+     * thread.
+     */
+    public final void addListener(LocationPowerSaveModeChangedListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener for changes to location power save mode.
+     */
+    public final void removeListener(LocationPowerSaveModeChangedListener listener) {
+        mListeners.remove(listener);
+    }
+
+    protected final void notifyLocationPowerSaveModeChanged(
+            @LocationPowerSaveMode int locationPowerSaveMode) {
+        for (LocationPowerSaveModeChangedListener listener : mListeners) {
+            listener.onLocationPowerSaveModeChanged(locationPowerSaveMode);
+        }
+    }
+
+    /**
+     * Returns the current location power save mode.
+     */
+    @LocationPowerSaveMode
+    public abstract int getLocationPowerSaveMode();
+}
diff --git a/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
new file mode 100644
index 0000000..d47bce3
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/ScreenInteractiveHelper.java
@@ -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.
+ */
+
+package com.android.server.location.util;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Provides accessors and listeners for screen interactive state (screen on/off).
+ */
+public abstract class ScreenInteractiveHelper {
+
+    /**
+     * Listener for screen interactive changes.
+     */
+    public interface ScreenInteractiveChangedListener {
+        /**
+         * Called when the screen interative state changes.
+         */
+        void onScreenInteractiveChanged(boolean isInteractive);
+    }
+
+    private final CopyOnWriteArrayList<ScreenInteractiveChangedListener> mListeners;
+
+    public ScreenInteractiveHelper() {
+        mListeners = new CopyOnWriteArrayList<>();
+    }
+
+    /**
+     * Add a listener for changes to screen interactive state. Callbacks occur on an unspecified
+     * thread.
+     */
+    public final void addListener(ScreenInteractiveChangedListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Removes a listener for changes to screen interactive state.
+     */
+    public final void removeListener(ScreenInteractiveChangedListener listener) {
+        mListeners.remove(listener);
+    }
+
+    protected final void notifyScreenInteractiveChanged(boolean interactive) {
+        for (ScreenInteractiveChangedListener listener : mListeners) {
+            listener.onScreenInteractiveChanged(interactive);
+        }
+    }
+
+    /**
+     * Returns true if the screen is currently interactive, and false otherwise.
+     */
+    public abstract boolean isInteractive();
+}
diff --git a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
index a953836..cfb7697 100644
--- a/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
+++ b/services/core/java/com/android/server/location/util/SystemAppOpsHelper.java
@@ -57,7 +57,7 @@
     }
 
     @Override
-    protected boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+    public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
         long identity = Binder.clearCallingIdentity();
@@ -75,7 +75,7 @@
     }
 
     @Override
-    protected void finishOp(int appOp, CallerIdentity callerIdentity) {
+    public void finishOp(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
         long identity = Binder.clearCallingIdentity();
@@ -91,7 +91,7 @@
     }
 
     @Override
-    protected boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+    public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
         long identity = Binder.clearCallingIdentity();
@@ -106,7 +106,7 @@
     }
 
     @Override
-    protected boolean noteOp(int appOp, CallerIdentity callerIdentity) {
+    public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
         long identity = Binder.clearCallingIdentity();
@@ -123,7 +123,7 @@
     }
 
     @Override
-    protected boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+    public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         Preconditions.checkState(mAppOps != null);
 
         long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java b/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.java
new file mode 100644
index 0000000..b9c0dde
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemLocationPermissionsHelper.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 com.android.server.location.util;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.content.Context;
+import android.location.util.identity.CallerIdentity;
+import android.os.Binder;
+
+import com.android.server.FgThread;
+
+/**
+ * Provides accessors and listeners for location permissions, including appops.
+ */
+public class SystemLocationPermissionsHelper extends LocationPermissionsHelper {
+
+    private final Context mContext;
+
+    private boolean mInited;
+
+    public SystemLocationPermissionsHelper(Context context, AppOpsHelper appOps) {
+        super(appOps);
+        mContext = context;
+    }
+
+    /** Called when system is ready. */
+    public void onSystemReady() {
+        if (mInited) {
+            return;
+        }
+
+        mContext.getPackageManager().addOnPermissionsChangeListener(
+                uid -> {
+                    // invoked on ui thread, move to fg thread so ui thread isn't blocked
+                    FgThread.getHandler().post(() -> notifyLocationPermissionsChanged(uid));
+                });
+        mInited = true;
+    }
+
+    @Override
+    protected boolean hasPermission(String permission, CallerIdentity callerIdentity) {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            return mContext.checkPermission(permission, callerIdentity.getPid(),
+                    callerIdentity.getUid()) == PERMISSION_GRANTED;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java b/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java
new file mode 100644
index 0000000..c8d8202
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemLocationPowerSaveModeHelper.java
@@ -0,0 +1,87 @@
+/*
+ * 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.util;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManager.LocationPowerSaveMode;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+import com.android.server.LocalServices;
+
+import java.util.Objects;
+import java.util.function.Consumer;
+
+/**
+ * Provides accessors and listeners for location power save mode.
+ */
+public class SystemLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper implements
+        Consumer<PowerSaveState> {
+
+    private final Context mContext;
+    private boolean mReady;
+
+    @LocationPowerSaveMode
+    private volatile int mLocationPowerSaveMode;
+
+    public SystemLocationPowerSaveModeHelper(Context context) {
+        mContext = context;
+    }
+
+    /** Called when system is ready. */
+    public void onSystemReady() {
+        if (mReady) {
+            return;
+        }
+
+        LocalServices.getService(PowerManagerInternal.class).registerLowPowerModeObserver(
+                PowerManager.ServiceType.LOCATION, this);
+        mLocationPowerSaveMode = Objects.requireNonNull(
+                mContext.getSystemService(PowerManager.class)).getLocationPowerSaveMode();
+
+        mReady = true;
+    }
+
+    @Override
+    public void accept(PowerSaveState powerSaveState) {
+        int locationPowerSaveMode;
+        if (!powerSaveState.batterySaverEnabled) {
+            locationPowerSaveMode = PowerManager.LOCATION_MODE_NO_CHANGE;
+        } else {
+            locationPowerSaveMode = powerSaveState.locationMode;
+        }
+
+        if (locationPowerSaveMode == mLocationPowerSaveMode) {
+            return;
+        }
+
+        mLocationPowerSaveMode = locationPowerSaveMode;
+
+        // invoked on ui thread, move to fg thread so we don't block the ui thread
+        FgThread.getHandler().post(() -> notifyLocationPowerSaveModeChanged(locationPowerSaveMode));
+    }
+
+    @LocationPowerSaveMode
+    @Override
+    public int getLocationPowerSaveMode() {
+        Preconditions.checkState(mReady);
+        return mLocationPowerSaveMode;
+    }
+}
diff --git a/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java b/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java
new file mode 100644
index 0000000..70cedac
--- /dev/null
+++ b/services/core/java/com/android/server/location/util/SystemScreenInteractiveHelper.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.util;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+
+/**
+ * Provides accessors and listeners for screen interactive state (screen on/off).
+ */
+public class SystemScreenInteractiveHelper extends ScreenInteractiveHelper {
+
+    private final Context mContext;
+
+    private boolean mReady;
+
+    private volatile boolean mIsInteractive;
+
+    public SystemScreenInteractiveHelper(Context context) {
+        mContext = context;
+    }
+
+    /** Called when system is ready. */
+    public void onSystemReady() {
+        if (mReady) {
+            return;
+        }
+
+        IntentFilter screenIntentFilter = new IntentFilter();
+        screenIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+        screenIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
+        mContext.registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                boolean interactive;
+                if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
+                    interactive = true;
+                } else if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+                    interactive = false;
+                } else {
+                    return;
+                }
+
+                onScreenInteractiveChanged(interactive);
+            }
+        }, UserHandle.ALL, screenIntentFilter, null, FgThread.getHandler());
+
+        mReady = true;
+    }
+
+    private void onScreenInteractiveChanged(boolean interactive) {
+        if (interactive == mIsInteractive) {
+            return;
+        }
+
+        mIsInteractive = interactive;
+        notifyScreenInteractiveChanged(interactive);
+    }
+
+    @Override
+    public boolean isInteractive() {
+        Preconditions.checkState(mReady);
+        return mIsInteractive;
+    }
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 0eba69e..9f6c18d 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -26,8 +26,8 @@
 import static com.android.server.media.MediaKeyDispatcher.isTripleTapOverridden;
 
 import android.app.ActivityManager;
-import android.app.INotificationManager;
 import android.app.KeyguardManager;
+import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
@@ -68,7 +68,6 @@
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
-import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -118,7 +117,7 @@
     private final SessionManagerImpl mSessionManagerImpl;
     private final MessageHandler mHandler = new MessageHandler();
     private final PowerManager.WakeLock mMediaEventWakeLock;
-    private final INotificationManager mNotificationManager;
+    private final NotificationManager mNotificationManager;
     private final Object mLock = new Object();
     private final HandlerThread mRecordThread = new HandlerThread("SessionRecordThread");
     // Keeps the full user id for each user.
@@ -158,10 +157,9 @@
         super(context);
         mContext = context;
         mSessionManagerImpl = new SessionManagerImpl();
-        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        PowerManager pm = mContext.getSystemService(PowerManager.class);
         mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
-        mNotificationManager = INotificationManager.Stub.asInterface(
-                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        mNotificationManager = mContext.getSystemService(NotificationManager.class);
     }
 
     @Override
@@ -507,11 +505,12 @@
     private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
             int resolvedUserId) {
         if (hasStatusBarServicePermission(pid, uid)) return;
+        // TODO: Refactor to use hasMediaControlPermission and hasEnabledNotificationListener
         if (mContext
                 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
                 != PackageManager.PERMISSION_GRANTED
                 && !isEnabledNotificationListener(compName,
-                UserHandle.getUserHandleForUid(uid).getIdentifier(), resolvedUserId)) {
+                UserHandle.getUserHandleForUid(uid), resolvedUserId)) {
             throw new SecurityException("Missing permission to control media.");
         }
     }
@@ -547,13 +546,13 @@
      * they're running as.
      *
      * @param compName The component that is enabled.
-     * @param userId The user id of the caller.
+     * @param userHandle The user handle of the caller.
      * @param forUserId The user id they're making the request on behalf of.
      * @return True if the component is enabled, false otherwise
      */
-    private boolean isEnabledNotificationListener(ComponentName compName, int userId,
+    private boolean isEnabledNotificationListener(ComponentName compName, UserHandle userHandle,
             int forUserId) {
-        if (userId != forUserId) {
+        if (userHandle.getIdentifier() != forUserId) {
             // You may not access another user's content as an enabled listener.
             return false;
         }
@@ -561,12 +560,8 @@
             Log.d(TAG, "Checking if enabled notification listener " + compName);
         }
         if (compName != null) {
-            try {
-                return mNotificationManager.isNotificationListenerAccessGrantedForUser(
-                        compName, userId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Dead NotificationManager in isEnabledNotificationListener", e);
-            }
+            return mNotificationManager.hasEnabledNotificationListener(compName.getPackageName(),
+                    userHandle);
         }
         return false;
     }
@@ -1922,8 +1917,8 @@
          * @param controllerUid uid of the controller app
          */
         @Override
-        public boolean isTrusted(String controllerPackageName, int controllerPid, int controllerUid)
-                throws RemoteException {
+        public boolean isTrusted(String controllerPackageName, int controllerPid,
+                int controllerUid) {
             final int uid = Binder.getCallingUid();
             final int userId = UserHandle.getUserHandleForUid(uid).getIdentifier();
             final long token = Binder.clearCallingIdentity();
@@ -1937,7 +1932,7 @@
                 // Context#getPackageName() for getting package name that matches with the PID/UID,
                 // but it doesn't tell which package has created the MediaController, so useless.
                 return hasMediaControlPermission(controllerPid, controllerUid)
-                        || hasEnabledNotificationListener(userId, controllerPackageName);
+                        || hasEnabledNotificationListener(userId, controllerPackageName, uid);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -2001,28 +1996,20 @@
             return resolvedUserId;
         }
 
-        private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName)
-                throws RemoteException {
+        private boolean hasEnabledNotificationListener(int resolvedUserId, String packageName,
+                int uid) {
+            // TODO: revisit this checking code
             // You may not access another user's content as an enabled listener.
             final int userId = UserHandle.getUserHandleForUid(resolvedUserId).getIdentifier();
             if (resolvedUserId != userId) {
                 return false;
             }
-
-            // TODO(jaewan): (Post-P) Propose NotificationManager#hasEnabledNotificationListener(
-            //               String pkgName) to notification team for optimization
-            final List<ComponentName> enabledNotificationListeners =
-                    mNotificationManager.getEnabledNotificationListeners(userId);
-            if (enabledNotificationListeners != null) {
-                for (int i = 0; i < enabledNotificationListeners.size(); i++) {
-                    if (TextUtils.equals(packageName,
-                            enabledNotificationListeners.get(i).getPackageName())) {
-                        return true;
-                    }
-                }
+            if (mNotificationManager.hasEnabledNotificationListener(packageName,
+                    UserHandle.getUserHandleForUid(uid))) {
+                return true;
             }
             if (DEBUG) {
-                Log.d(TAG, packageName + " (uid=" + resolvedUserId + ") doesn't have an enabled "
+                Log.d(TAG, packageName + " (uid=" + uid + ") doesn't have an enabled "
                         + "notification listener");
             }
             return false;
diff --git a/services/core/java/com/android/server/media/OWNERS b/services/core/java/com/android/server/media/OWNERS
index b460cb5..2e2d812 100644
--- a/services/core/java/com/android/server/media/OWNERS
+++ b/services/core/java/com/android/server/media/OWNERS
@@ -2,6 +2,7 @@
 hdmoon@google.com
 insun@google.com
 jaewan@google.com
+jinpark@google.com
 klhyun@google.com
 lajos@google.com
 sungsoo@google.com
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 88964e0..cf48e02 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -274,6 +274,7 @@
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.server.wm.BackgroundActivityStartCallback;
 import com.android.server.wm.WindowManagerInternal;
 
 import libcore.io.IoUtils;
@@ -515,7 +516,7 @@
 
     private static final int MY_UID = Process.myUid();
     private static final int MY_PID = Process.myPid();
-    private static final IBinder WHITELIST_TOKEN = new Binder();
+    private static final IBinder ALLOWLIST_TOKEN = new Binder();
     protected RankingHandler mRankingHandler;
     private long mLastOverRateLogTime;
     private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
@@ -1722,7 +1723,7 @@
         super(context);
         mNotificationRecordLogger = notificationRecordLogger;
         mNotificationInstanceIdSequence = notificationInstanceIdSequence;
-        Notification.processWhitelistToken = WHITELIST_TOKEN;
+        Notification.processAllowlistToken = ALLOWLIST_TOKEN;
     }
 
     // TODO - replace these methods with new fields in the VisibleForTesting constructor
@@ -1894,6 +1895,7 @@
                 (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
         mAm = am;
         mAtm = atm;
+        mAtm.setBackgroundActivityStartCallback(new NotificationTrampolineCallback());
         mUgm = ugm;
         mUgmInternal = ugmInternal;
         mPackageManager = packageManager;
@@ -4752,6 +4754,12 @@
         }
 
         @Override
+        public boolean hasEnabledNotificationListener(String packageName, int userId) {
+            checkCallerIsSystem();
+            return mListeners.isPackageAllowed(packageName, userId);
+        }
+
+        @Override
         public boolean isNotificationListenerAccessGranted(ComponentName listener) {
             Objects.requireNonNull(listener);
             checkCallerIsSystemOrSameApp(listener.getPackageName());
@@ -5775,21 +5783,21 @@
             mShortcutHelper.cacheShortcut(info, user);
         }
 
-        // Whitelist pending intents.
+        // temporarily allow apps to perform extra work when their pending intents are launched
         if (notification.allPendingIntents != null) {
             final int intentCount = notification.allPendingIntents.size();
             if (intentCount > 0) {
                 final ActivityManagerInternal am = LocalServices
                         .getService(ActivityManagerInternal.class);
                 final long duration = LocalServices.getService(
-                        DeviceIdleInternal.class).getNotificationWhitelistDuration();
+                        DeviceIdleInternal.class).getNotificationAllowlistDuration();
                 for (int i = 0; i < intentCount; i++) {
                     PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
                     if (pendingIntent != null) {
                         am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
-                                WHITELIST_TOKEN, duration);
+                                ALLOWLIST_TOKEN, duration);
                         am.setPendingIntentAllowBgActivityStarts(pendingIntent.getTarget(),
-                                WHITELIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER
+                                ALLOWLIST_TOKEN, (FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER
                                         | FLAG_SERVICE_SENDER));
                     }
                 }
@@ -7642,7 +7650,7 @@
                     // make sure deleteIntent cannot be used to start activities from background
                     LocalServices.getService(ActivityManagerInternal.class)
                             .clearPendingIntentAllowBgActivityStarts(deleteIntent.getTarget(),
-                            WHITELIST_TOKEN);
+                                    ALLOWLIST_TOKEN);
                     deleteIntent.send();
                 } catch (PendingIntent.CanceledException ex) {
                     // do nothing - there's no relevant way to recover, and
@@ -9918,4 +9926,36 @@
         if (TextUtils.isEmpty(val)) return defValue;
         return Boolean.parseBoolean(val);
     }
+
+    /**
+     * Shows a warning on logcat. Shows the toast only once per package. This is to avoid being too
+     * aggressive and annoying the user.
+     *
+     * TODO(b/161957908): Remove dogfooder toast.
+     */
+    private class NotificationTrampolineCallback implements BackgroundActivityStartCallback {
+        private Set<String> mPackagesShown = new ArraySet<>();
+
+        @Override
+        public IBinder getToken() {
+            return WHITELIST_TOKEN;
+        }
+
+        @Override
+        public void onExclusiveTokenActivityStart(String packageName) {
+            Slog.w(TAG, "Indirect notification activity start from " + packageName);
+            boolean isFirstOccurrence = mPackagesShown.add(packageName);
+            if (!isFirstOccurrence) {
+                return;
+            }
+
+            mUiHandler.post(() ->
+                    Toast.makeText(getUiContext(),
+                            "Indirect activity start from "
+                                    + packageName + ". "
+                                    + "This will be blocked in S.\n"
+                                    + "See go/s-trampolines.",
+                            Toast.LENGTH_LONG).show());
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/notification/RankingReconsideration.java b/services/core/java/com/android/server/notification/RankingReconsideration.java
index 057f0f1..9b046b1 100644
--- a/services/core/java/com/android/server/notification/RankingReconsideration.java
+++ b/services/core/java/com/android/server/notification/RankingReconsideration.java
@@ -90,7 +90,7 @@
 
     /**
      * Apply any computed changes to the notification record.  This method will be
-     * called on the main service thread, synchronized on he mNotificationList.
+     * called on the main service thread, synchronized on the mNotificationList.
      * @param record The locked record to be updated.
      */
     public abstract void applyChangesLocked(NotificationRecord record);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2854b33..2a59893a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6445,9 +6445,14 @@
                     true /*allowDynamicSplits*/);
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
+            final boolean queryMayBeFiltered =
+                    UserHandle.getAppId(filterCallingUid) >= Process.FIRST_APPLICATION_UID
+                            && !resolveForStart;
+
             final ResolveInfo bestChoice =
                     chooseBestActivity(
-                            intent, resolvedType, flags, privateResolveFlags, query, userId);
+                            intent, resolvedType, flags, privateResolveFlags, query, userId,
+                            queryMayBeFiltered);
             final boolean nonBrowserOnly =
                     (privateResolveFlags & PackageManagerInternal.RESOLVE_NON_BROWSER_ONLY) != 0;
             if (nonBrowserOnly && bestChoice != null && bestChoice.handleAllWebDataURI) {
@@ -6611,7 +6616,8 @@
     }
 
     private ResolveInfo chooseBestActivity(Intent intent, String resolvedType,
-            int flags, int privateResolveFlags, List<ResolveInfo> query, int userId) {
+            int flags, int privateResolveFlags, List<ResolveInfo> query, int userId,
+            boolean queryMayBeFiltered) {
         if (query != null) {
             final int N = query.size();
             if (N == 1) {
@@ -6636,7 +6642,7 @@
                 // If we have saved a preference for a preferred activity for
                 // this Intent, use that.
                 ResolveInfo ri = findPreferredActivityNotLocked(intent, resolvedType,
-                        flags, query, r0.priority, true, false, debug, userId);
+                        flags, query, r0.priority, true, false, debug, userId, queryMayBeFiltered);
                 if (ri != null) {
                     return ri;
                 }
@@ -6818,11 +6824,19 @@
                 && intent.hasCategory(CATEGORY_DEFAULT);
     }
 
+    ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
+            List<ResolveInfo> query, int priority, boolean always,
+            boolean removeMatches, boolean debug, int userId) {
+        return findPreferredActivityNotLocked(
+                intent, resolvedType, flags, query, priority, always, removeMatches, debug, userId,
+                UserHandle.getAppId(Binder.getCallingUid()) >= Process.FIRST_APPLICATION_UID);
+    }
+
     // TODO: handle preferred activities missing while user has amnesia
     /** <b>must not hold {@link #mLock}</b> */
     ResolveInfo findPreferredActivityNotLocked(Intent intent, String resolvedType, int flags,
             List<ResolveInfo> query, int priority, boolean always,
-            boolean removeMatches, boolean debug, int userId) {
+            boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {
         if (Thread.holdsLock(mLock)) {
             Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
                     + " is holding mLock", new Throwable());
@@ -6916,10 +6930,12 @@
                         }
                         final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent)
                                 && !isDeviceProvisioned;
+                        final boolean allowSetMutation = !excludeSetupWizardHomeActivity
+                                && !queryMayBeFiltered;
                         if (ai == null) {
                             // Do not remove launcher's preferred activity during SetupWizard
                             // due to it may not install yet
-                            if (excludeSetupWizardHomeActivity) {
+                            if (!allowSetMutation) {
                                 continue;
                             }
 
@@ -6944,7 +6960,7 @@
                                 continue;
                             }
 
-                            if (removeMatches) {
+                            if (removeMatches && allowSetMutation) {
                                 pir.removeFilter(pa);
                                 changed = true;
                                 if (DEBUG_PREFERRED) {
@@ -6961,7 +6977,7 @@
 
                             if (always && !pa.mPref.sameSet(query, excludeSetupWizardHomeActivity)) {
                                 if (pa.mPref.isSuperset(query, excludeSetupWizardHomeActivity)) {
-                                    if (!excludeSetupWizardHomeActivity) {
+                                    if (allowSetMutation) {
                                         // some components of the set are no longer present in
                                         // the query, but the preferred activity can still be reused
                                         if (DEBUG_PREFERRED) {
@@ -6982,24 +6998,28 @@
                                         changed = true;
                                     } else {
                                         if (DEBUG_PREFERRED) {
-                                            Slog.i(TAG, "Do not remove preferred activity for launcher"
-                                                    + " during SetupWizard");
+                                            Slog.i(TAG, "Do not remove preferred activity");
                                         }
                                     }
                                 } else {
-                                    Slog.i(TAG,
-                                            "Result set changed, dropping preferred activity for "
-                                                    + intent + " type " + resolvedType);
-                                    if (DEBUG_PREFERRED) {
-                                        Slog.v(TAG, "Removing preferred activity since set changed "
-                                                + pa.mPref.mComponent);
+                                    if (allowSetMutation) {
+                                        Slog.i(TAG,
+                                                "Result set changed, dropping preferred activity "
+                                                        + "for " + intent + " type "
+                                                        + resolvedType);
+                                        if (DEBUG_PREFERRED) {
+                                            Slog.v(TAG,
+                                                    "Removing preferred activity since set changed "
+                                                            + pa.mPref.mComponent);
+                                        }
+                                        pir.removeFilter(pa);
+                                        // Re-add the filter as a "last chosen" entry (!always)
+                                        PreferredActivity lastChosen = new PreferredActivity(
+                                                pa, pa.mPref.mMatch, null, pa.mPref.mComponent,
+                                                false);
+                                        pir.addFilter(lastChosen);
+                                        changed = true;
                                     }
-                                    pir.removeFilter(pa);
-                                    // Re-add the filter as a "last chosen" entry (!always)
-                                    PreferredActivity lastChosen = new PreferredActivity(
-                                            pa, pa.mPref.mMatch, null, pa.mPref.mComponent, false);
-                                    pir.addFilter(lastChosen);
-                                    changed = true;
                                     return null;
                                 }
                             }
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index ed57067..361a506 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -34,6 +34,7 @@
 import android.os.ParcelableException;
 import android.os.RemoteCallback;
 import android.os.UserHandle;
+import android.os.UserManagerInternal;
 import android.os.storage.StorageManagerInternal;
 import android.os.storage.StorageVolume;
 import android.service.storage.ExternalStorageService;
@@ -62,19 +63,25 @@
 public final class StorageUserConnection {
     private static final String TAG = "StorageUserConnection";
 
-    public static final int REMOTE_TIMEOUT_SECONDS = 20;
+    private static final int DEFAULT_REMOTE_TIMEOUT_SECONDS = 20;
+    // TODO(b/161702661): Workaround for demo user to have shorter timeout.
+    // This allows the DevicePolicyManagerService#enableSystemApp call to succeed without ANR.
+    private static final int DEMO_USER_REMOTE_TIMEOUT_SECONDS = 5;
 
     private final Object mLock = new Object();
     private final Context mContext;
     private final int mUserId;
     private final StorageSessionController mSessionController;
     private final ActiveConnection mActiveConnection = new ActiveConnection();
+    private final boolean mIsDemoUser;
     @GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>();
 
     public StorageUserConnection(Context context, int userId, StorageSessionController controller) {
         mContext = Objects.requireNonNull(context);
         mUserId = Preconditions.checkArgumentNonnegative(userId);
         mSessionController = controller;
+        mIsDemoUser = LocalServices.getService(UserManagerInternal.class)
+                .getUserInfo(userId).isDemo();
     }
 
     /**
@@ -200,7 +207,8 @@
 
     private void waitForLatch(CountDownLatch latch, String reason) throws TimeoutException {
         try {
-            if (!latch.await(REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
+            if (!latch.await(mIsDemoUser ? DEMO_USER_REMOTE_TIMEOUT_SECONDS
+                            : DEFAULT_REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
                 // TODO(b/140025078): Call ActivityManager ANR API?
                 Slog.wtf(TAG, "Failed to bind to the ExternalStorageService for user " + mUserId);
                 throw new TimeoutException("Latch wait for " + reason + " elapsed");
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 2394baf..fd3c1f9 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -1469,7 +1469,7 @@
             if (userId > 0) {
                 return userId;
             } else {
-                Slog.wtf(TAG, "EXTRA_USER_HANDLE missing or invalid, value=" + userId);
+                Log.w(TAG, "EXTRA_USER_HANDLE missing or invalid, value=" + userId);
                 return -100;
             }
         }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 55962fc..90f87b1 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1160,9 +1160,7 @@
             }
         };
 
-        private Runnable mTryToRebindRunnable = () -> {
-            tryToRebind();
-        };
+        private Runnable mTryToRebindRunnable = this::tryToRebind;
 
         WallpaperConnection(WallpaperInfo info, WallpaperData wallpaper, int clientUid) {
             mInfo = info;
@@ -1295,14 +1293,14 @@
                     // a short time in the future, specifically to allow any pending package
                     // update message on this same looper thread to be processed.
                     if (!mWallpaper.wallpaperUpdating) {
-                        mContext.getMainThreadHandler().postDelayed(() -> processDisconnect(this),
+                        mContext.getMainThreadHandler().postDelayed(mDisconnectRunnable,
                                 1000);
                     }
                 }
             }
         }
 
-        public void scheduleTimeoutLocked() {
+        private void scheduleTimeoutLocked() {
             // If we didn't reset it right away, do so after we couldn't connect to
             // it for an extended amount of time to avoid having a black wallpaper.
             final Handler fgHandler = FgThread.getHandler();
@@ -1342,11 +1340,11 @@
             }
         }
 
-        private void processDisconnect(final ServiceConnection connection) {
+        private Runnable mDisconnectRunnable = () -> {
             synchronized (mLock) {
                 // The wallpaper disappeared.  If this isn't a system-default one, track
                 // crashes and fall back to default if it continues to misbehave.
-                if (connection == mWallpaper.connection) {
+                if (this == mWallpaper.connection) {
                     final ComponentName wpService = mWallpaper.wallpaperComponent;
                     if (!mWallpaper.wallpaperUpdating
                             && mWallpaper.userId == mCurrentUserId
@@ -1374,7 +1372,7 @@
                     }
                 }
             }
-        }
+        };
 
         /**
          * Called by a live wallpaper if its colors have changed.
@@ -2786,6 +2784,13 @@
                     WallpaperConnection.DisplayConnector::disconnectLocked);
             wallpaper.connection.mService = null;
             wallpaper.connection.mDisplayConnector.clear();
+
+            FgThread.getHandler().removeCallbacks(wallpaper.connection.mResetRunnable);
+            mContext.getMainThreadHandler().removeCallbacks(
+                    wallpaper.connection.mDisconnectRunnable);
+            mContext.getMainThreadHandler().removeCallbacks(
+                    wallpaper.connection.mTryToRebindRunnable);
+
             wallpaper.connection = null;
             if (wallpaper == mLastWallpaper) mLastWallpaper = null;
         }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index a903bcd..777ddda 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -221,6 +221,14 @@
             boolean allowBackgroundActivityStart);
 
     /**
+     * Callback to be called on certain activity start scenarios.
+     *
+     * @see BackgroundActivityStartCallback
+     */
+    public abstract void setBackgroundActivityStartCallback(
+            @Nullable BackgroundActivityStartCallback callback);
+
+    /**
      * Start activity {@code intent} without calling user-id check.
      *
      * - DO NOT call it with the calling UID cleared.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 029b554..c4af3e2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -145,7 +145,6 @@
 import android.app.IApplicationThread;
 import android.app.IAssistDataReceiver;
 import android.app.INotificationManager;
-import android.app.IRequestFinishCallback;
 import android.app.ITaskStackListener;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -675,6 +674,9 @@
     WindowOrganizerController mWindowOrganizerController;
     TaskOrganizerController mTaskOrganizerController;
 
+    @Nullable
+    private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
+
     private int mDeviceOwnerUid = Process.INVALID_UID;
 
     private final class FontScaleSettingObserver extends ContentObserver {
@@ -1001,6 +1003,11 @@
         return config;
     }
 
+    @Nullable
+    public BackgroundActivityStartCallback getBackgroundActivityStartCallback() {
+        return mBackgroundActivityStartCallback;
+    }
+
     private void start() {
         LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
     }
@@ -2464,7 +2471,7 @@
     }
 
     @Override
-    public void onBackPressedOnTaskRoot(IBinder token, IRequestFinishCallback callback) {
+    public void onBackPressedOnTaskRoot(IBinder token) {
         synchronized (mGlobalLock) {
             ActivityRecord r = ActivityRecord.isInStackLocked(token);
             if (r == null) {
@@ -2478,18 +2485,13 @@
                 // callback
             } else if (stack != null && (stack.isSingleTaskInstance())) {
                 // Single-task stacks are used for activities which are presented in floating
-                // windows above full screen activities. Instead of directly finishing the
-                // task, a task change listener is used to notify SystemUI so the action can be
-                // handled specially.
+                // windows above full screen activities. A task change listener is used to notify
+                // SystemUI so the back action can be handled specially.
                 final Task task = r.getTask();
                 mTaskChangeNotificationController
                         .notifyBackPressedOnTaskRoot(task.getTaskInfo());
             } else {
-                try {
-                    callback.requestFinish();
-                } catch (RemoteException e) {
-                    Slog.e(TAG, "Failed to invoke request finish callback", e);
-                }
+                moveActivityTaskToBack(token, false /* nonRoot */);
             }
         }
     }
@@ -2835,7 +2837,7 @@
         }
 
         if (toTop) {
-            taskDisplayArea.positionStackAt(POSITION_TOP, primarySplitTask,
+            taskDisplayArea.positionChildAt(POSITION_TOP, primarySplitTask,
                     false /* includingParents */);
         }
         WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -6142,6 +6144,11 @@
             return false;
         }
 
+        public void setBackgroundActivityStartCallback(
+                @Nullable BackgroundActivityStartCallback backgroundActivityStartCallback) {
+            mBackgroundActivityStartCallback = backgroundActivityStartCallback;
+        }
+
         @Override
         public int startActivitiesAsPackage(String packageName, @Nullable String featureId,
                 int userId, Intent[] intents, Bundle bOptions) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
new file mode 100644
index 0000000..4e742b9
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.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.server.wm;
+
+import android.os.IBinder;
+
+/**
+ * Callback to be called when a background activity start is allowed exclusively because of the
+ * token provided in {@link #getToken()}.
+ */
+public interface BackgroundActivityStartCallback {
+    /**
+     * The token that allowed the activity start that triggered {@link
+     * #onExclusiveTokenActivityStart()}.
+     *
+     * Ideally this should just return a final variable, don't do anything costly here (don't hold
+     * any locks).
+     */
+    IBinder getToken();
+
+    /**
+     * Called when the background activity start happens.
+     */
+    void onExclusiveTokenActivityStart(String packageName);
+}
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 98f57c5..0d8e882 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -368,7 +368,7 @@
     /** Sets the always on top flag for this configuration container.
      *  When you call this function, make sure that the following functions are called as well to
      *  keep proper z-order.
-     *  - {@Link DisplayContent#positionStackAt(POSITION_TOP, TaskStack)};
+     *  - {@link TaskDisplayArea#positionChildAt(int POSITION_TOP, Task, boolean)};
      * */
     public void setAlwaysOnTop(boolean alwaysOnTop) {
         mRequestsTmpConfig.setTo(getRequestedOverrideConfiguration());
diff --git a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
index c7cba77..e2c0749 100644
--- a/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
+++ b/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
@@ -16,8 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.Task.TAG_VISIBILITY;
 
@@ -174,12 +172,7 @@
         }
 
         final int windowingMode = mContiner.getWindowingMode();
-        if (windowingMode == WINDOWING_MODE_FREEFORM) {
-            // The visibility of tasks and the activities they contain in freeform stack are
-            // determined individually unlike other stacks where the visibility or fullscreen
-            // status of an activity in a previous task affects other.
-            mBehindFullscreenActivity = !mContainerShouldBeVisible;
-        } else if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
+        if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
                 && r.isRootOfTask()) {
             if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + mContiner
                     + " stackShouldBeVisible=" + mContainerShouldBeVisible
diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java
index 3b39b6b..852b3672 100644
--- a/services/core/java/com/android/server/wm/InputConsumerImpl.java
+++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java
@@ -88,6 +88,7 @@
         mWindowHandle.ownerUid = Process.myUid();
         mWindowHandle.inputFeatures = 0;
         mWindowHandle.scaleFactor = 1.0f;
+        mWindowHandle.trustedOverlay = true;
 
         mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId).getSession())
                 .setContainerLayer()
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 0216db4..20f1b9f 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -27,7 +27,18 @@
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
 import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_CONSUMER;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
+import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE;
 import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
@@ -67,9 +78,6 @@
     private boolean mUpdateInputWindowsPending;
     private boolean mUpdateInputWindowsImmediately;
 
-    // Currently focused input window handle.
-    private InputWindowHandle mFocusedInputWindowHandle;
-
     private boolean mDisableWallpaperTouchEvents;
     private final Rect mTmpRect = new Rect();
     private final UpdateInputForAllWindowsConsumer mUpdateInputForAllWindowsConsumer;
@@ -309,10 +317,6 @@
             Slog.d(TAG_WM, "addInputWindowHandle: "
                     + child + ", " + inputWindowHandle);
         }
-
-        if (hasFocus) {
-            mFocusedInputWindowHandle = inputWindowHandle;
-        }
     }
 
     void setUpdateInputWindowsNeededLw() {
@@ -578,6 +582,7 @@
         inputWindowHandle.portalToDisplayId = INVALID_DISPLAY;
         inputWindowHandle.touchableRegion.setEmpty();
         inputWindowHandle.setTouchableRegionCrop(null);
+        inputWindowHandle.trustedOverlay = isTrustedOverlay(type);
     }
 
     /**
@@ -592,4 +597,17 @@
         populateOverlayInputInfo(inputWindowHandle, name, TYPE_SECURE_SYSTEM_OVERLAY, true);
         t.setInputWindowInfo(sc, inputWindowHandle);
     }
+
+    static boolean isTrustedOverlay(int type) {
+        return type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY
+                || type == TYPE_INPUT_METHOD || type == TYPE_INPUT_METHOD_DIALOG
+                || type == TYPE_MAGNIFICATION_OVERLAY || type == TYPE_STATUS_BAR
+                || type == TYPE_NOTIFICATION_SHADE
+                || type == TYPE_NAVIGATION_BAR
+                || type == TYPE_NAVIGATION_BAR_PANEL
+                || type == TYPE_SECURE_SYSTEM_OVERLAY
+                || type == TYPE_DOCK_DIVIDER
+                || type == TYPE_ACCESSIBILITY_OVERLAY
+                || type == TYPE_INPUT_CONSUMER;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e6e92ea..821bd70 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2459,7 +2459,7 @@
             // Since always on top is only on when the stack is freeform or pinned, the state
             // can be toggled when the windowing mode changes. We must make sure the stack is
             // placed properly when always on top state changes.
-            taskDisplayArea.positionStackAtTop(this, false /* includingParents */);
+            taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */);
         }
     }
 
@@ -5340,7 +5340,8 @@
         }
 
         if (isRootTask()) {
-            taskDisplayArea.positionStackAtTop(this, false /* includingParents */, reason);
+            taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */,
+                    reason);
         }
         if (task == null) {
             task = this;
@@ -5368,7 +5369,8 @@
             if (parentTask != null) {
                 parentTask.moveToBack(reason, this);
             } else {
-                displayArea.positionStackAtBottom(this, reason);
+                displayArea.positionChildAt(POSITION_BOTTOM, this, false /*includingParents*/,
+                        reason);
             }
             if (task != null && task != this) {
                 positionChildAtBottom(task);
@@ -7448,7 +7450,7 @@
         // always on top windows. Since the position the stack should be inserted into is calculated
         // properly in {@link DisplayContent#getTopInsertPosition()} in both cases, we can just
         // request that the stack is put at top here.
-        taskDisplayArea.positionStackAtTop(this, false /* includingParents */);
+        taskDisplayArea.positionChildAt(POSITION_TOP, this, false /* includingParents */);
     }
 
     /** NOTE: Should only be called from {@link Task#reparent}. */
@@ -7493,7 +7495,7 @@
             final Task task = getBottomMostTask();
             setWindowingMode(WINDOWING_MODE_UNDEFINED);
 
-            getDisplayArea().positionStackAtTop(this, false /* includingParents */);
+            getDisplayArea().positionChildAt(POSITION_TOP, this, false /* includingParents */);
 
             mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
             MetricsLoggerWrapper.logPictureInPictureFullScreen(mAtmService.mContext,
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 4473bd6..246753a 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -303,15 +303,18 @@
     }
 
     @Override
-    void addChild(Task stack, int position) {
-        if (DEBUG_STACK) Slog.d(TAG_WM, "Set stack=" + stack + " on taskDisplayArea=" + this);
-        addStackReferenceIfNeeded(stack);
-        position = findPositionForStack(position, stack, true /* adding */);
+    void addChild(Task task, int position) {
+        if (DEBUG_STACK) Slog.d(TAG_WM, "Set task=" + task + " on taskDisplayArea=" + this);
+        if (mDisplayContent.mSingleTaskInstance && getStackCount() == 1) {
+            throw new IllegalStateException("addChild: Can only have one task on display=" + this);
+        }
 
-        super.addChild(stack, position);
+        addStackReferenceIfNeeded(task);
+        position = findPositionForStack(position, task, true /* adding */);
+
+        super.addChild(task, position);
         mAtmService.updateSleepIfNeededLocked();
-
-        positionStackAt(stack, position);
+        onStackOrderChanged(task);
     }
 
     @Override
@@ -328,19 +331,42 @@
         return true;
     }
 
+    void positionChildAt(int position, Task child, boolean includingParents,
+            String updateLastFocusedTaskReason) {
+        final Task prevFocusedTask = updateLastFocusedTaskReason != null ? getFocusedStack() : null;
+
+        positionChildAt(position, child, includingParents);
+
+        if (updateLastFocusedTaskReason == null) {
+            return;
+        }
+
+        final Task currentFocusedStack = getFocusedStack();
+        if (currentFocusedStack == prevFocusedTask) {
+            return;
+        }
+
+        mLastFocusedStack = prevFocusedTask;
+        EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser,
+                mDisplayContent.mDisplayId,
+                currentFocusedStack == null ? -1 : currentFocusedStack.getRootTaskId(),
+                mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
+                updateLastFocusedTaskReason);
+    }
+
     @Override
     void positionChildAt(int position, Task child, boolean includingParents) {
         final boolean moveToTop = position >= getChildCount() - 1;
         final boolean moveToBottom = position <= 0;
 
+        final int oldPosition = mChildren.indexOf(child);
         if (child.getWindowConfiguration().isAlwaysOnTop() && !moveToTop) {
             // This stack is always-on-top, override the default behavior.
             Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom");
 
             // Moving to its current position, as we must call super but we don't want to
             // perform any meaningful action.
-            final int currentPosition = mChildren.indexOf(child);
-            super.positionChildAt(currentPosition, child, false /* includingParents */);
+            super.positionChildAt(oldPosition, child, false /* includingParents */);
             return;
         }
         // We don't allow untrusted display to top when task stack moves to top,
@@ -358,7 +384,7 @@
 
         child.updateTaskMovement(moveToTop);
 
-        mDisplayContent.setLayoutNeeded();
+        mDisplayContent.layoutAndAssignWindowLayersIfNeeded();
 
         // The insert position may be adjusted to non-top when there is always-on-top stack. Since
         // the original position is preferred to be top, the stack should have higher priority when
@@ -373,6 +399,10 @@
 
         // Update the top resumed activity because the preferred top focusable task may be changed.
         mAtmService.mStackSupervisor.updateTopResumedActivityIfNeeded();
+
+        if (mChildren.indexOf(child) != oldPosition) {
+            onStackOrderChanged(child);
+        }
     }
 
     @Override
@@ -800,66 +830,6 @@
         }
     }
 
-    void positionStackAt(int position, Task child, boolean includingParents) {
-        positionChildAt(position, child, includingParents);
-        mDisplayContent.layoutAndAssignWindowLayersIfNeeded();
-    }
-
-    void positionStackAtTop(Task stack, boolean includingParents) {
-        positionStackAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
-    }
-
-    void positionStackAtTop(Task stack, boolean includingParents,
-            String updateLastFocusedStackReason) {
-        positionStackAt(stack, getStackCount(), includingParents,
-                updateLastFocusedStackReason);
-    }
-
-    void positionStackAtBottom(Task stack) {
-        positionStackAtBottom(stack, null /* updateLastFocusedStackReason */);
-    }
-
-    void positionStackAtBottom(Task stack, String updateLastFocusedStackReason) {
-        positionStackAt(stack, 0, false /* includingParents */,
-                updateLastFocusedStackReason);
-    }
-
-    void positionStackAt(Task stack, int position) {
-        positionStackAt(stack, position, false /* includingParents */,
-                null /* updateLastFocusedStackReason */);
-    }
-
-    void positionStackAt(Task stack, int position, boolean includingParents,
-            String updateLastFocusedStackReason) {
-        // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
-        //       the position internally, also update the logic here
-        final Task prevFocusedStack = updateLastFocusedStackReason != null
-                ? getFocusedStack() : null;
-        final boolean wasContained = mChildren.contains(stack);
-        if (mDisplayContent.mSingleTaskInstance && getStackCount() == 1 && !wasContained) {
-            throw new IllegalStateException(
-                    "positionStackAt: Can only have one task on display=" + this);
-        }
-
-        // Since positionChildAt() is called during the creation process of pinned stacks,
-        // ActivityStack#getStack() can be null.
-        positionStackAt(position, stack, includingParents);
-
-        if (updateLastFocusedStackReason != null) {
-            final Task currentFocusedStack = getFocusedStack();
-            if (currentFocusedStack != prevFocusedStack) {
-                mLastFocusedStack = prevFocusedStack;
-                EventLogTags.writeWmFocusedStack(mRootWindowContainer.mCurrentUser,
-                        mDisplayContent.mDisplayId,
-                        currentFocusedStack == null ? -1 : currentFocusedStack.getRootTaskId(),
-                        mLastFocusedStack == null ? -1 : mLastFocusedStack.getRootTaskId(),
-                        updateLastFocusedStackReason);
-            }
-        }
-
-        onStackOrderChanged(stack);
-    }
-
     /**
      * Moves/reparents `task` to the back of whatever container the home stack is in. This is for
      * when we just want to move a task to "the back" vs. a specific place. The primary use-case
@@ -872,7 +842,7 @@
         if (homeParentTask == null) {
             // reparent throws if parent didn't change...
             if (task.getParent() == this) {
-                positionStackAtBottom(task);
+                positionChildAt(POSITION_BOTTOM, task, false /*includingParents*/);
             } else {
                 task.reparent(this, false /* onTop */);
             }
@@ -1091,7 +1061,7 @@
         if (launchRootTask != null) {
             launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
             if (onTop) {
-                positionStackAtTop(launchRootTask, false /* includingParents */);
+                positionChildAt(POSITION_TOP, launchRootTask, false /* includingParents */);
             }
         } else {
             addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
@@ -1666,15 +1636,11 @@
             return;
         }
 
-        final boolean isRootTask = stack.isRootTask();
-        if (isRootTask) {
-            // Move the stack to the bottom to not affect the following visibility checks
-            positionStackAtBottom(stack);
-        } else {
-            stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
-        }
+        // Move the stack to the bottom to not affect the following visibility checks
+        stack.getParent().positionChildAt(POSITION_BOTTOM, stack, false /* includingParents */);
 
         // Find the next position where the stack should be placed
+        final boolean isRootTask = stack.isRootTask();
         final int numStacks = isRootTask ? getStackCount() : stack.getParent().getChildCount();
         for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
             final Task s = isRootTask ? getStackAt(stackNdx)
@@ -1688,11 +1654,7 @@
             if (s.shouldBeVisible(null) && isValidWindowingMode) {
                 // Move the provided stack to behind this stack
                 final int position = Math.max(0, stackNdx - 1);
-                if (isRootTask) {
-                    positionStackAt(stack, position);
-                } else {
-                    stack.getParent().positionChildAt(position, stack, false /*includingParents */);
-                }
+                stack.getParent().positionChildAt(position, stack, false /*includingParents */);
                 break;
             }
         }
@@ -1722,11 +1684,7 @@
         final int insertIndex = stackIndex <= behindStackIndex
                 ? behindStackIndex - 1 : behindStackIndex;
         final int position = Math.max(0, insertIndex);
-        if (stack.isRootTask()) {
-            positionStackAt(stack, position);
-        } else {
-            parent.positionChildAt(position, stack, false /* includingParents */);
-        }
+        parent.positionChildAt(position, stack, false /* includingParents */);
     }
 
     boolean hasPinnedTask() {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 4c5ac93..548345e 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -345,29 +345,17 @@
                     final Task rootTask = (Task) (
                             (newParent != null && !(newParent instanceof TaskDisplayArea))
                                     ? newParent : task.getRootTask());
-                    if (hop.getToTop()) {
-                        as.getDisplayArea().positionStackAtTop(rootTask,
-                                false /* includingParents */);
-                    } else {
-                        as.getDisplayArea().positionStackAtBottom(rootTask);
-                    }
+                    as.getDisplayArea().positionChildAt(
+                            hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, rootTask,
+                            false /* includingParents */);
                 }
             } else {
                 throw new RuntimeException("Reparenting leaf Tasks is not supported now. " + task);
             }
         } else {
-            // Ugh, of course ActivityStack has its own special reorder logic...
-            if (task.isRootTask()) {
-                if (hop.getToTop()) {
-                    as.getDisplayArea().positionStackAtTop(as, false /* includingParents */);
-                } else {
-                    as.getDisplayArea().positionStackAtBottom(as);
-                }
-            } else {
-                task.getParent().positionChildAt(
-                        hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
-                        task, false /* includingParents */);
-            }
+            task.getParent().positionChildAt(
+                    hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
+                    task, false /* includingParents */);
         }
         return TRANSACT_EFFECTS_LIFECYCLE;
     }
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index da9c7f3..6ba8769 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -55,11 +55,14 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Configuration;
+import android.os.Binder;
 import android.os.Build;
+import android.os.IBinder;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Slog;
@@ -98,6 +101,11 @@
     final ApplicationInfo mInfo;
     final String mName;
     final int mUid;
+
+    // A set of tokens that currently contribute to this process being temporarily allowed
+    // to start activities even if it's not in the foreground. The values of this map are optional
+    // (can be null) and are used to trace back the grant to the notification token mechanism.
+    private final ArrayMap<Binder, IBinder> mBackgroundActivityStartTokens = new ArrayMap<>();
     // The process of this application; 0 if none
     private volatile int mPid;
     // user of process.
@@ -160,9 +168,6 @@
     private volatile boolean mPerceptible;
     // Set to true when process was launched with a wrapper attached
     private volatile boolean mUsingWrapper;
-    // Set to true if this process is currently temporarily allowed to start activities even if
-    // it's not in the foreground
-    private volatile boolean mAllowBackgroundActivityStarts;
     // Set of UIDs of clients currently bound to this process
     private volatile ArraySet<Integer> mBoundClientUids = new ArraySet<Integer>();
 
@@ -208,6 +213,9 @@
     /** Whether our process is currently running a {@link IRemoteAnimationRunner} */
     private boolean mRunningRemoteAnimation;
 
+    @Nullable
+    private BackgroundActivityStartCallback mBackgroundActivityStartCallback;
+
     public WindowProcessController(@NonNull ActivityTaskManagerService atm, ApplicationInfo info,
             String name, int uid, int userId, Object owner, WindowProcessListener listener) {
         mInfo = info;
@@ -218,6 +226,7 @@
         mListener = listener;
         mAtm = atm;
         mDisplayId = INVALID_DISPLAY;
+        mBackgroundActivityStartCallback = mAtm.getBackgroundActivityStartCallback();
 
         boolean isSysUiPackage = info.packageName.equals(
                 mAtm.getSysUiServiceComponentLocked().getPackageName());
@@ -449,19 +458,39 @@
         mLastActivityFinishTime = finishTime;
     }
 
-    public void setAllowBackgroundActivityStarts(boolean allowBackgroundActivityStarts) {
-        mAllowBackgroundActivityStarts = allowBackgroundActivityStarts;
+    /**
+     * Allows background activity starts using token {@code entity}. Optionally, you can provide
+     * {@code originatingToken} if you have one such originating token, this is useful for tracing
+     * back the grant in the case of the notification token.
+     */
+    public void addAllowBackgroundActivityStartsToken(Binder entity,
+            @Nullable IBinder originatingToken) {
+        synchronized (mAtm.mGlobalLock) {
+            mBackgroundActivityStartTokens.put(entity, originatingToken);
+        }
+    }
+
+    /**
+     * Removes token {@code entity} that allowed background activity starts added via {@link
+     * #addAllowBackgroundActivityStartsToken(Binder, IBinder)}.
+     */
+    public void removeAllowBackgroundActivityStartsToken(Binder entity) {
+        synchronized (mAtm.mGlobalLock) {
+            mBackgroundActivityStartTokens.remove(entity);
+        }
+    }
+
+    /**
+     * Returns true if background activity starts are allowed by any token added via {@link
+     * #addAllowBackgroundActivityStartsToken(Binder, IBinder)}.
+     */
+    public boolean areBackgroundActivityStartsAllowedByToken() {
+        synchronized (mAtm.mGlobalLock) {
+            return !mBackgroundActivityStartTokens.isEmpty();
+        }
     }
 
     boolean areBackgroundActivityStartsAllowed() {
-        // allow if the flag was explicitly set
-        if (mAllowBackgroundActivityStarts) {
-            if (DEBUG_ACTIVITY_STARTS) {
-                Slog.d(TAG, "[WindowProcessController(" + mPid
-                        + ")] Activity start allowed: mAllowBackgroundActivityStarts = true");
-            }
-            return true;
-        }
         // allow if any activity in the caller has either started or finished very recently, and
         // it must be started or finished after last stop app switches time.
         final long now = SystemClock.uptimeMillis();
@@ -510,9 +539,32 @@
             }
             return true;
         }
+        // allow if the flag was explicitly set
+        if (!mBackgroundActivityStartTokens.isEmpty()) {
+            onBackgroundStartAllowedByToken();
+            if (DEBUG_ACTIVITY_STARTS) {
+                Slog.d(TAG, "[WindowProcessController(" + mPid
+                        + ")] Activity start allowed: process allowed by token");
+            }
+            return true;
+        }
         return false;
     }
 
+    private void onBackgroundStartAllowedByToken() {
+        if (mBackgroundActivityStartCallback == null) {
+            return;
+        }
+        IBinder callbackToken = mBackgroundActivityStartCallback.getToken();
+        for (IBinder token : mBackgroundActivityStartTokens.values()) {
+            if (token != callbackToken) {
+                return;
+            }
+        }
+        mAtm.mH.post(() ->
+                mBackgroundActivityStartCallback.onExclusiveTokenActivityStart(mInfo.packageName));
+    }
+
     private boolean isBoundByForegroundUid() {
         for (int i = mBoundClientUids.size() - 1; i >= 0; --i) {
             if (mAtm.isUidForeground(mBoundClientUids.valueAt(i))) {
@@ -1434,6 +1486,13 @@
             if (mVrThreadTid != 0) {
                 pw.print(prefix); pw.print("mVrThreadTid="); pw.println(mVrThreadTid);
             }
+            if (mBackgroundActivityStartTokens.size() > 0) {
+                pw.print(prefix); pw.println("Background activity start tokens:");
+                for (int i = 0; i < mBackgroundActivityStartTokens.size(); i++) {
+                    pw.print(prefix); pw.print("  - ");
+                    pw.println(mBackgroundActivityStartTokens.keyAt(i));
+                }
+            }
         }
         pw.println(prefix + " Configuration=" + getConfiguration());
         pw.println(prefix + " OverrideConfiguration=" + getRequestedOverrideConfiguration());
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 9ab1572..f3c5092 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -63,7 +63,6 @@
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
@@ -88,7 +87,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_ADDITIONAL;
 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
@@ -954,17 +952,7 @@
         mInputWindowHandle.trustedOverlay =
                 (mAttrs.privateFlags & PRIVATE_FLAG_TRUSTED_OVERLAY) != 0
                 && mOwnerCanAddInternalSystemWindow;
-        mInputWindowHandle.trustedOverlay |=
-                mAttrs.type == TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY
-                || mAttrs.type == TYPE_INPUT_METHOD || mAttrs.type == TYPE_INPUT_METHOD_DIALOG
-                || mAttrs.type == TYPE_MAGNIFICATION_OVERLAY || mAttrs.type == TYPE_STATUS_BAR
-                || mAttrs.type == TYPE_NOTIFICATION_SHADE
-                || mAttrs.type == TYPE_NAVIGATION_BAR
-                || mAttrs.type == TYPE_NAVIGATION_BAR_PANEL
-                || mAttrs.type == TYPE_SECURE_SYSTEM_OVERLAY
-                || mAttrs.type == TYPE_DOCK_DIVIDER
-                || mAttrs.type == TYPE_ACCESSIBILITY_OVERLAY
-                || mAttrs.type == TYPE_INPUT_CONSUMER;
+        mInputWindowHandle.trustedOverlay |= InputMonitor.isTrustedOverlay(mAttrs.type);
 
         // Make sure we initial all fields before adding to parentWindow, to prevent exception
         // during onDisplayChanged.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index c6b93d6..7ec819f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3570,6 +3570,17 @@
         return new JournaledFile(new File(base), new File(base + ".tmp"));
     }
 
+    /**
+     * Persist modified values to disk by calling {@link #saveSettingsLocked} for each
+     * affected user ID.
+     */
+    @GuardedBy("getLockObject()")
+    private void saveSettingsForUsersLocked(Set<Integer> affectedUserIds) {
+        for (int userId : affectedUserIds) {
+            saveSettingsLocked(userId);
+        }
+    }
+
     private void saveSettingsLocked(int userHandle) {
         DevicePolicyData policy = getUserData(userHandle);
         JournaledFile journal = makeJournaledFile(userHandle);
@@ -4785,13 +4796,15 @@
 
     /**
      * Updates a flag that tells us whether the user's password currently satisfies the
-     * requirements set by all of the user's active admins. The flag is updated both in memory
-     * and persisted to disk by calling {@link #saveSettingsLocked}, for the value of the flag
-     * be the correct one upon boot.
-     * This should be called whenever the password or the admin policies have changed.
+     * requirements set by all of the user's active admins.
+     * This should be called whenever the password or the admin policies have changed. The caller
+     * is responsible for calling {@link #saveSettingsLocked} to persist the change.
+     *
+     * @return the set of user IDs that have been affected
      */
     @GuardedBy("getLockObject()")
-    private void updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
+    private Set<Integer> updatePasswordValidityCheckpointLocked(int userHandle, boolean parent) {
+        final ArraySet<Integer> affectedUserIds = new ArraySet<>();
         final int credentialOwner = getCredentialOwner(userHandle, parent);
         DevicePolicyData policy = getUserData(credentialOwner);
         PasswordMetrics metrics = mLockSettingsInternal.getUserPasswordMetrics(credentialOwner);
@@ -4801,9 +4814,10 @@
                     metrics, userHandle, parent);
             if (newCheckpoint != policy.mPasswordValidAtLastCheckpoint) {
                 policy.mPasswordValidAtLastCheckpoint = newCheckpoint;
-                saveSettingsLocked(credentialOwner);
+                affectedUserIds.add(credentialOwner);
             }
         }
+        return affectedUserIds;
     }
 
     /**
@@ -6175,7 +6189,8 @@
         }
     }
 
-    private void removeCaApprovalsIfNeeded(int userId) {
+    private Set<Integer> removeCaApprovalsIfNeeded(int userId) {
+        final ArraySet<Integer> affectedUserIds = new ArraySet<>();
         for (UserInfo userInfo : mUserManager.getProfiles(userId)) {
             boolean isSecure = mLockPatternUtils.isSecure(userInfo.id);
             if (userInfo.isManagedProfile()){
@@ -6184,11 +6199,12 @@
             if (!isSecure) {
                 synchronized (getLockObject()) {
                     getUserData(userInfo.id).mAcceptedCaCertificates.clear();
-                    saveSettingsLocked(userInfo.id);
+                    affectedUserIds.add(userInfo.id);
                 }
                 mCertificateMonitor.onCertificateApprovalsChanged(userId);
             }
         }
+        return affectedUserIds;
     }
 
     @Override
@@ -7458,42 +7474,45 @@
         }
 
         DevicePolicyData policy = getUserData(userId);
+        final ArraySet<Integer> affectedUserIds = new ArraySet<>();
 
         synchronized (getLockObject()) {
             policy.mFailedPasswordAttempts = 0;
-            updatePasswordValidityCheckpointLocked(userId, /* parent */ false);
-            saveSettingsLocked(userId);
-            updatePasswordExpirationsLocked(userId);
+            affectedUserIds.add(userId);
+            affectedUserIds.addAll(updatePasswordValidityCheckpointLocked(
+                    userId, /* parent */ false));
+            affectedUserIds.addAll(updatePasswordExpirationsLocked(userId));
             setExpirationAlarmCheckLocked(mContext, userId, /* parent */ false);
 
             // Send a broadcast to each profile using this password as its primary unlock.
             sendAdminCommandForLockscreenPoliciesLocked(
                     DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
                     DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userId);
+
+            affectedUserIds.addAll(removeCaApprovalsIfNeeded(userId));
+            saveSettingsForUsersLocked(affectedUserIds);
         }
-        removeCaApprovalsIfNeeded(userId);
     }
 
     /**
      * Called any time the device password is updated. Resets all password expiration clocks.
+     *
+     * @return the set of user IDs that have been affected
      */
-    private void updatePasswordExpirationsLocked(int userHandle) {
-        ArraySet<Integer> affectedUserIds = new ArraySet<Integer>();
+    private Set<Integer> updatePasswordExpirationsLocked(int userHandle) {
+        final ArraySet<Integer> affectedUserIds = new ArraySet<>();
         List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(
                 userHandle, /* parent */ false);
-        final int N = admins.size();
-        for (int i = 0; i < N; i++) {
+        for (int i = 0; i < admins.size(); i++) {
             ActiveAdmin admin = admins.get(i);
             if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
                 affectedUserIds.add(admin.getUserHandle().getIdentifier());
                 long timeout = admin.passwordExpirationTimeout;
-                long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
-                admin.passwordExpirationDate = expiration;
+                admin.passwordExpirationDate =
+                        timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
             }
         }
-        for (int affectedUserId : affectedUserIds) {
-            saveSettingsLocked(affectedUserId);
-        }
+        return affectedUserIds;
     }
 
     @Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
index da794da..e947e89 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeAppOpsHelper.java
@@ -57,7 +57,7 @@
     }
 
     @Override
-    protected boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+    public boolean startOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
         if (!myAppOp.mAllowed) {
             return false;
@@ -68,20 +68,20 @@
     }
 
     @Override
-    protected void finishOp(int appOp, CallerIdentity callerIdentity) {
+    public void finishOp(int appOp, CallerIdentity callerIdentity) {
         AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
         Preconditions.checkState(myAppOp.mStarted);
         myAppOp.mStarted = false;
     }
 
     @Override
-    protected boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+    public boolean checkOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
         return myAppOp.mAllowed;
     }
 
     @Override
-    protected boolean noteOp(int appOp, CallerIdentity callerIdentity) {
+    public boolean noteOp(int appOp, CallerIdentity callerIdentity) {
         if (!noteOpNoThrow(appOp, callerIdentity)) {
             throw new SecurityException(
                     "noteOp not allowed for op " + appOp + " and caller " + callerIdentity);
@@ -91,7 +91,7 @@
     }
 
     @Override
-    protected boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
+    public boolean noteOpNoThrow(int appOp, CallerIdentity callerIdentity) {
         AppOp myAppOp = getOp(callerIdentity.getPackageName(), appOp);
         if (!myAppOp.mAllowed) {
             return false;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java
new file mode 100644
index 0000000..e7d7e31
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPermissionsHelper.java
@@ -0,0 +1,56 @@
+/*
+ * 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.util;
+
+import android.location.util.identity.CallerIdentity;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Version of LocationPermissionsHelper for testing. All permissions are granted unless notified
+ * otherwise.
+ */
+public class FakeLocationPermissionsHelper extends LocationPermissionsHelper {
+
+    private final HashMap<String, Set<String>> mRevokedPermissions;
+
+    public FakeLocationPermissionsHelper(AppOpsHelper appOps) {
+        super(appOps);
+        mRevokedPermissions = new HashMap<>();
+    }
+
+    public void grantPermission(String packageName, String permission) {
+        getRevokedPermissionsList(packageName).remove(permission);
+        notifyLocationPermissionsChanged(packageName);
+    }
+
+    public void revokePermission(String packageName, String permission) {
+        getRevokedPermissionsList(packageName).add(permission);
+        notifyLocationPermissionsChanged(packageName);
+    }
+
+    @Override
+    protected boolean hasPermission(String permission, CallerIdentity identity) {
+        return !getRevokedPermissionsList(identity.getPackageName()).contains(permission);
+    }
+
+    private Set<String> getRevokedPermissionsList(String packageName) {
+        return mRevokedPermissions.computeIfAbsent(packageName, p -> new HashSet<>());
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java
new file mode 100644
index 0000000..3ead5d4
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeLocationPowerSaveModeHelper.java
@@ -0,0 +1,49 @@
+/*
+ * 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.util;
+
+import android.os.IPowerManager;
+import android.os.PowerManager.LocationPowerSaveMode;
+
+/**
+ * Version of LocationPowerSaveModeHelper for testing. Power save mode is initialized as "no
+ * change".
+ */
+public class FakeLocationPowerSaveModeHelper extends LocationPowerSaveModeHelper {
+
+    @LocationPowerSaveMode
+    private int mLocationPowerSaveMode;
+
+    public FakeLocationPowerSaveModeHelper() {
+        mLocationPowerSaveMode = IPowerManager.LOCATION_MODE_NO_CHANGE;
+    }
+
+    public void setLocationPowerSaveMode(int locationPowerSaveMode) {
+        if (locationPowerSaveMode == mLocationPowerSaveMode) {
+            return;
+        }
+
+        mLocationPowerSaveMode = locationPowerSaveMode;
+        notifyLocationPowerSaveModeChanged(locationPowerSaveMode);
+    }
+
+    @LocationPowerSaveMode
+    @Override
+    public int getLocationPowerSaveMode() {
+        return mLocationPowerSaveMode;
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.java
new file mode 100644
index 0000000..df697fa
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeScreenInteractiveHelper.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.server.location.util;
+
+/**
+ * Version of ScreenInteractiveHelper for testing. Screen is initialized as interactive (on).
+ */
+public class FakeScreenInteractiveHelper extends ScreenInteractiveHelper {
+
+    private boolean mIsInteractive;
+
+    public FakeScreenInteractiveHelper() {
+        mIsInteractive = true;
+    }
+
+    public void setScreenInteractive(boolean interactive) {
+        if (interactive == mIsInteractive) {
+            return;
+        }
+
+        mIsInteractive = interactive;
+        notifyScreenInteractiveChanged(interactive);
+    }
+
+    public boolean isInteractive() {
+        return mIsInteractive;
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
index 726b1b8..1d0523f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeSettingsHelper.java
@@ -90,7 +90,7 @@
 
     @Override
     public boolean isLocationEnabled(int userId) {
-        return mLocationEnabledSetting.getValue(Boolean.class);
+        return mLocationEnabledSetting.getValue(userId, Boolean.class);
     }
 
     @Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
index 336e28c..f5978da 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/FakeUserInfoHelper.java
@@ -16,7 +16,6 @@
 
 package com.android.server.location.util;
 
-import android.os.Process;
 import android.util.IndentingPrintWriter;
 import android.util.IntArray;
 import android.util.SparseArray;
@@ -27,19 +26,21 @@
 import java.io.FileDescriptor;
 
 /**
- * Version of UserInfoHelper for testing. The user this code is running under is set as the current
- * user by default, with no profiles.
+ * Version of UserInfoHelper for testing. By default there is one user that starts in a running
+ * state with a userId of 0;
  */
 public class FakeUserInfoHelper extends UserInfoHelper {
 
+    public static final int DEFAULT_USERID = 0;
+
     private final IntArray mRunningUserIds;
     private final SparseArray<IntArray> mProfiles;
 
     private int mCurrentUserId;
 
     public FakeUserInfoHelper() {
-        mCurrentUserId = Process.myUserHandle().getIdentifier();
-        mRunningUserIds = IntArray.wrap(new int[]{mCurrentUserId});
+        mCurrentUserId = DEFAULT_USERID;
+        mRunningUserIds = IntArray.wrap(new int[]{DEFAULT_USERID});
         mProfiles = new SparseArray<>();
     }
 
@@ -67,6 +68,10 @@
         dispatchOnUserStopped(userId);
     }
 
+    public void setCurrentUserId(int parentUser) {
+        setCurrentUserIds(parentUser, new int[]{parentUser});
+    }
+
     public void setCurrentUserIds(int parentUser, int[] currentProfileUserIds) {
         Preconditions.checkArgument(ArrayUtils.contains(currentProfileUserIds, parentUser));
         int oldUserId = mCurrentUserId;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
index e6f6252..4165b6e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/LocationAttributionHelperTest.java
@@ -16,7 +16,11 @@
 
 package com.android.server.location.util;
 
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -46,9 +50,7 @@
     public void setUp() {
         initMocks(this);
 
-        when(mAppOpsHelper.startLocationMonitoring(any(CallerIdentity.class))).thenReturn(true);
-        when(mAppOpsHelper.startHighPowerLocationMonitoring(any(CallerIdentity.class))).thenReturn(
-                true);
+        when(mAppOpsHelper.startOpNoThrow(anyInt(), any(CallerIdentity.class))).thenReturn(true);
 
         mHelper = new LocationAttributionHelper(mAppOpsHelper);
     }
@@ -63,30 +65,30 @@
         Object key4 = new Object();
 
         mHelper.reportLocationStart(caller1, "gps", key1);
-        verify(mAppOpsHelper).startLocationMonitoring(caller1);
-        verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
 
         mHelper.reportLocationStart(caller1, "gps", key2);
-        verify(mAppOpsHelper).startLocationMonitoring(caller1);
-        verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller1);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
 
         mHelper.reportLocationStart(caller2, "gps", key3);
-        verify(mAppOpsHelper).startLocationMonitoring(caller2);
-        verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
 
         mHelper.reportLocationStart(caller2, "gps", key4);
-        verify(mAppOpsHelper).startLocationMonitoring(caller2);
-        verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_LOCATION, caller2);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
 
         mHelper.reportLocationStop(caller1, "gps", key2);
-        verify(mAppOpsHelper, never()).stopLocationMonitoring(caller1);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller1);
         mHelper.reportLocationStop(caller1, "gps", key1);
-        verify(mAppOpsHelper).stopLocationMonitoring(caller1);
+        verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller1);
 
         mHelper.reportLocationStop(caller2, "gps", key3);
-        verify(mAppOpsHelper, never()).stopLocationMonitoring(caller2);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_LOCATION, caller2);
         mHelper.reportLocationStop(caller2, "gps", key4);
-        verify(mAppOpsHelper).stopLocationMonitoring(caller2);
+        verify(mAppOpsHelper).finishOp(OP_MONITOR_LOCATION, caller2);
     }
 
     @Test
@@ -99,29 +101,29 @@
         Object key4 = new Object();
 
         mHelper.reportHighPowerLocationStart(caller1, "gps", key1);
-        verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller1);
-        verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
 
         mHelper.reportHighPowerLocationStart(caller1, "gps", key2);
-        verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller1);
-        verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
 
         mHelper.reportHighPowerLocationStart(caller2, "gps", key3);
-        verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller2);
-        verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
 
         mHelper.reportHighPowerLocationStart(caller2, "gps", key4);
-        verify(mAppOpsHelper).startHighPowerLocationMonitoring(caller2);
-        verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+        verify(mAppOpsHelper).startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
 
         mHelper.reportHighPowerLocationStop(caller1, "gps", key2);
-        verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller1);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
         mHelper.reportHighPowerLocationStop(caller1, "gps", key1);
-        verify(mAppOpsHelper).stopHighPowerLocationMonitoring(caller1);
+        verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller1);
 
         mHelper.reportHighPowerLocationStop(caller2, "gps", key3);
-        verify(mAppOpsHelper, never()).stopHighPowerLocationMonitoring(caller2);
+        verify(mAppOpsHelper, never()).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
         mHelper.reportHighPowerLocationStop(caller2, "gps", key4);
-        verify(mAppOpsHelper).stopHighPowerLocationMonitoring(caller2);
+        verify(mAppOpsHelper).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, caller2);
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
index f40d316..093aa2e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemAppOpsHelperTest.java
@@ -20,12 +20,8 @@
 import static android.app.AppOpsManager.OP_COARSE_LOCATION;
 import static android.app.AppOpsManager.OP_FINE_LOCATION;
 import static android.app.AppOpsManager.OP_MOCK_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
 import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
 
-import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
-import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.ArgumentMatchers.any;
@@ -34,10 +30,12 @@
 import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.MockitoAnnotations.initMocks;
+import static org.testng.Assert.assertThrows;
 
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -105,41 +103,41 @@
     }
 
     @Test
-    public void testCheckLocationAccess() {
+    public void testCheckOp() {
         CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
 
         doReturn(MODE_ALLOWED).when(
                 mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
-        assertThat(mHelper.checkLocationAccess(identity, PERMISSION_FINE)).isTrue();
+        assertThat(mHelper.checkOpNoThrow(OP_FINE_LOCATION, identity)).isTrue();
 
         doReturn(MODE_IGNORED).when(
                 mAppOps).checkOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"));
-        assertThat(mHelper.checkLocationAccess(identity, PERMISSION_FINE)).isFalse();
+        assertThat(mHelper.checkOpNoThrow(OP_FINE_LOCATION, identity)).isFalse();
 
         identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
 
         doReturn(MODE_ALLOWED).when(
                 mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
-        assertThat(mHelper.checkLocationAccess(identity, PERMISSION_COARSE)).isTrue();
+        assertThat(mHelper.checkOpNoThrow(OP_COARSE_LOCATION, identity)).isTrue();
 
         doReturn(MODE_IGNORED).when(
                 mAppOps).checkOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"));
-        assertThat(mHelper.checkLocationAccess(identity, PERMISSION_COARSE)).isFalse();
+        assertThat(mHelper.checkOpNoThrow(OP_COARSE_LOCATION, identity)).isFalse();
     }
 
     @Test
-    public void testNoteLocationAccess() {
+    public void testNoteOpNoThrow() {
         CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
 
         doReturn(MODE_ALLOWED).when(
                 mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
                         eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.noteLocationAccess(identity, PERMISSION_FINE)).isTrue();
+        assertThat(mHelper.noteOpNoThrow(OP_FINE_LOCATION, identity)).isTrue();
 
         doReturn(MODE_IGNORED).when(
                 mAppOps).noteOpNoThrow(eq(OP_FINE_LOCATION), eq(1000), eq("mypackage"),
                         eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.noteLocationAccess(identity, PERMISSION_FINE)).isFalse();
+        assertThat(mHelper.noteOpNoThrow(OP_FINE_LOCATION, identity)).isFalse();
 
 
         identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
@@ -147,74 +145,55 @@
         doReturn(MODE_ALLOWED).when(
                 mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
                         eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.noteLocationAccess(identity, PERMISSION_COARSE)).isTrue();
+        assertThat(mHelper.noteOpNoThrow(OP_COARSE_LOCATION, identity)).isTrue();
 
         doReturn(MODE_IGNORED).when(
                 mAppOps).noteOpNoThrow(eq(OP_COARSE_LOCATION), eq(1000), eq("mypackage"),
                         eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.noteLocationAccess(identity, PERMISSION_COARSE)).isFalse();
+        assertThat(mHelper.noteOpNoThrow(OP_COARSE_LOCATION, identity)).isFalse();
     }
 
     @Test
-    public void testStartLocationMonitoring() {
+    public void testStartOp() {
         CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
 
         doReturn(MODE_ALLOWED).when(
                 mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"),
                         eq(false), eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.startLocationMonitoring(identity)).isTrue();
+        assertThat(mHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)).isTrue();
 
         doReturn(MODE_IGNORED).when(
                 mAppOps).startOpNoThrow(eq(OP_MONITOR_LOCATION), eq(1000), eq("mypackage"),
                         eq(false), eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.startLocationMonitoring(identity)).isFalse();
+        assertThat(mHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)).isFalse();
     }
 
     @Test
-    public void testStartHighPowerLocationMonitoring() {
+    public void testFinishOp() {
         CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
 
-        doReturn(MODE_ALLOWED).when(
-                mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
-                        eq("mypackage"),
-                        eq(false), eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isTrue();
-
-        doReturn(MODE_IGNORED).when(
-                mAppOps).startOpNoThrow(eq(OP_MONITOR_HIGH_POWER_LOCATION), eq(1000),
-                        eq("mypackage"),
-                        eq(false), eq("myfeature"), nullable(String.class));
-        assertThat(mHelper.startHighPowerLocationMonitoring(identity)).isFalse();
-    }
-
-    @Test
-    public void testStopLocationMonitoring() {
-        CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
-
-        mHelper.stopLocationMonitoring(identity);
+        mHelper.finishOp(OP_MONITOR_LOCATION, identity);
         verify(mAppOps).finishOp(OP_MONITOR_LOCATION, 1000, "mypackage", "myfeature");
     }
 
     @Test
-    public void testStopHighPowerLocationMonitoring() {
-        CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
-
-        mHelper.stopHighPowerLocationMonitoring(identity);
-        verify(mAppOps).finishOp(OP_MONITOR_HIGH_POWER_LOCATION, 1000, "mypackage", "myfeature");
-    }
-
-    @Test
-    public void testNoteMockLocationAccess() {
+    public void testNoteOp() {
         CallerIdentity identity = CallerIdentity.forTest(1000, 1000, "mypackage", "myfeature");
 
         doReturn(MODE_ALLOWED).when(
                 mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
                         nullable(String.class));
-        assertThat(mHelper.noteMockLocationAccess(identity)).isTrue();
+        assertThat(mHelper.noteOp(OP_MOCK_LOCATION, identity)).isTrue();
 
         doReturn(MODE_IGNORED).when(
                 mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
                         nullable(String.class));
-        assertThat(mHelper.noteMockLocationAccess(identity)).isFalse();
+        assertThat(mHelper.noteOp(OP_MOCK_LOCATION, identity)).isFalse();
+
+
+        doThrow(new SecurityException()).when(
+                mAppOps).noteOp(eq(OP_MOCK_LOCATION), eq(1000), eq("mypackage"), eq("myfeature"),
+                nullable(String.class));
+        assertThrows(SecurityException.class, () -> mHelper.noteOp(OP_MOCK_LOCATION, identity));
     }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java
new file mode 100644
index 0000000..2acb70c
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/SystemLocationPowerSaveModeHelperTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.util;
+
+import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
+import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
+import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF;
+import static android.os.PowerManager.LOCATION_MODE_NO_CHANGE;
+import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.after;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.PowerSaveState;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.location.util.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SystemLocationPowerSaveModeHelperTest {
+
+    private static final long TIMEOUT_MS = 5000;
+    private static final long FAILURE_TIMEOUT_MS = 200;
+
+    @Mock
+    private PowerManagerInternal mPowerManagerInternal;
+
+    private List<Consumer<PowerSaveState>> mListeners = new ArrayList<>();
+
+    private SystemLocationPowerSaveModeHelper mHelper;
+
+    @Before
+    public void setUp() {
+        initMocks(this);
+
+        LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternal);
+
+        doAnswer(invocation -> mListeners.add(invocation.getArgument(1))).when(
+                mPowerManagerInternal).registerLowPowerModeObserver(anyInt(), any(Consumer.class));
+
+        PowerManager powerManager = mock(PowerManager.class);
+        doReturn(LOCATION_MODE_NO_CHANGE).when(powerManager).getLocationPowerSaveMode();
+        Context context = mock(Context.class);
+        doReturn(powerManager).when(context).getSystemService(PowerManager.class);
+
+        mHelper = new SystemLocationPowerSaveModeHelper(context);
+        mHelper.onSystemReady();
+    }
+
+    @After
+    public void tearDown() {
+        LocalServices.removeServiceForTest(PowerManagerInternal.class);
+    }
+
+    private void sendPowerSaveState(PowerSaveState powerSaveState) {
+        for (Consumer<PowerSaveState> listener : mListeners) {
+            listener.accept(powerSaveState);
+        }
+    }
+
+    @Test
+    public void testListener() {
+        LocationPowerSaveModeChangedListener listener = mock(
+                LocationPowerSaveModeChangedListener.class);
+        mHelper.addListener(listener);
+
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(false).build());
+        verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+                anyInt());
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(false).build());
+        verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+                anyInt());
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(false).build());
+        verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+                anyInt());
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_FOREGROUND_ONLY).setBatterySaverEnabled(false).build());
+        verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+                anyInt());
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF).setBatterySaverEnabled(
+                false).build());
+        verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+                anyInt());
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(true).build());
+        verify(listener, after(FAILURE_TIMEOUT_MS).never()).onLocationPowerSaveModeChanged(
+                anyInt());
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(true).build());
+        verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+                LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF);
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+                LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF).setBatterySaverEnabled(true).build());
+        verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+                LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+                LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_FOREGROUND_ONLY).setBatterySaverEnabled(true).build());
+        verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+                LOCATION_MODE_FOREGROUND_ONLY);
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_FOREGROUND_ONLY);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF).setBatterySaverEnabled(
+                true).build());
+        verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+                LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(
+                LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF);
+        sendPowerSaveState(new PowerSaveState.Builder().setLocationMode(
+                LOCATION_MODE_NO_CHANGE).setBatterySaverEnabled(true).build());
+        verify(listener, timeout(TIMEOUT_MS)).onLocationPowerSaveModeChanged(
+                LOCATION_MODE_NO_CHANGE);
+        assertThat(mHelper.getLocationPowerSaveMode()).isEqualTo(LOCATION_MODE_NO_CHANGE);
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
index c22dc10..1867be0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/util/TestInjector.java
@@ -16,22 +16,32 @@
 
 package com.android.server.location.util;
 
+import com.android.server.location.LocationRequestStatistics;
+
 public class TestInjector implements Injector {
 
     private final FakeUserInfoHelper mUserInfoHelper;
     private final FakeAppOpsHelper mAppOpsHelper;
+    private final FakeLocationPermissionsHelper mLocationPermissionsHelper;
     private final FakeSettingsHelper mSettingsHelper;
     private final FakeAppForegroundHelper mAppForegroundHelper;
-    private final LocationUsageLogger mLocationUsageLogger;
+    private final FakeLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
+    private final FakeScreenInteractiveHelper mScreenInteractiveHelper;
     private final LocationAttributionHelper mLocationAttributionHelper;
+    private final LocationUsageLogger mLocationUsageLogger;
+    private final LocationRequestStatistics mLocationRequestStatistics;
 
     public TestInjector() {
         mUserInfoHelper = new FakeUserInfoHelper();
         mAppOpsHelper = new FakeAppOpsHelper();
+        mLocationPermissionsHelper = new FakeLocationPermissionsHelper(mAppOpsHelper);
         mSettingsHelper = new FakeSettingsHelper();
         mAppForegroundHelper = new FakeAppForegroundHelper();
-        mLocationUsageLogger = new LocationUsageLogger();
+        mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper();
+        mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
         mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
+        mLocationUsageLogger = new LocationUsageLogger();
+        mLocationRequestStatistics = new LocationRequestStatistics();
     }
 
     @Override
@@ -45,6 +55,11 @@
     }
 
     @Override
+    public FakeLocationPermissionsHelper getLocationPermissionsHelper() {
+        return mLocationPermissionsHelper;
+    }
+
+    @Override
     public FakeSettingsHelper getSettingsHelper() {
         return mSettingsHelper;
     }
@@ -55,12 +70,27 @@
     }
 
     @Override
-    public LocationUsageLogger getLocationUsageLogger() {
-        return mLocationUsageLogger;
+    public FakeLocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
+        return mLocationPowerSaveModeHelper;
+    }
+
+    @Override
+    public FakeScreenInteractiveHelper getScreenInteractiveHelper() {
+        return mScreenInteractiveHelper;
     }
 
     @Override
     public LocationAttributionHelper getLocationAttributionHelper() {
         return mLocationAttributionHelper;
     }
+
+    @Override
+    public LocationUsageLogger getLocationUsageLogger() {
+        return mLocationUsageLogger;
+    }
+
+    @Override
+    public LocationRequestStatistics getLocationRequestStatistics() {
+        return mLocationRequestStatistics;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
index a871ec6..5bef877 100644
--- a/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -195,7 +195,8 @@
                 false /* sticky */,
                 false /* initialSticky */,
                 userId,
-                false, /* allowBackgroundActivityStarts */
+                false /* allowBackgroundActivityStarts */,
+                null /* activityStartsToken */,
                 false /* timeoutExempt */ );
     }
 }
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 daaabf8..9a465a91 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -48,8 +48,6 @@
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isNull;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
@@ -4931,20 +4929,7 @@
                 .thenReturn(passwordMetrics);
         dpm.reportPasswordChanged(userHandle);
 
-        // Drain ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED broadcasts as part of
-        // reportPasswordChanged()
-        // This broadcast should be sent 2-4 times:
-        // * Twice from calls to DevicePolicyManagerService.updatePasswordExpirationsLocked,
-        //   once for each affected user, in DevicePolicyManagerService.reportPasswordChanged.
-        // * Optionally, at most twice from calls to DevicePolicyManagerService.saveSettingsLocked
-        //   in DevicePolicyManagerService.reportPasswordChanged, once with the userId
-        //   the password change is relevant to and another with the credential owner of said
-        //   userId, if the password checkpoint value changes.
-        verify(mContext.spiedContext, atMost(4)).sendBroadcastAsUser(
-                MockUtils.checkIntentAction(
-                        DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
-                MockUtils.checkUserHandle(userHandle));
-        verify(mContext.spiedContext, atLeast(2)).sendBroadcastAsUser(
+        verify(mContext.spiedContext, times(1)).sendBroadcastAsUser(
                 MockUtils.checkIntentAction(
                         DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
                 MockUtils.checkUserHandle(userHandle));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 16aa87b..10976882 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -385,7 +385,7 @@
         MockitoAnnotations.initMocks(this);
 
         DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class);
-        when(deviceIdleInternal.getNotificationWhitelistDuration()).thenReturn(3000L);
+        when(deviceIdleInternal.getNotificationAllowlistDuration()).thenReturn(3000L);
         ActivityManagerInternal activityManagerInternal = mock(ActivityManagerInternal.class);
 
         LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
index 0006193..fcff228 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationShellCmdTest.java
@@ -215,20 +215,14 @@
             "Charlotte"
     };
     static final String[] MESSAGES = {
-            "Shall I compare thee to a summer's day?",
-            "Thou art more lovely and more temperate:",
-            "Rough winds do shake the darling buds of May,",
-            "And summer's lease hath all too short a date;",
-            "Sometime too hot the eye of heaven shines,",
-            "And often is his gold complexion dimm'd;",
-            "And every fair from fair sometime declines,",
-            "By chance or nature's changing course untrimm'd;",
-            "But thy eternal summer shall not fade,",
-            "Nor lose possession of that fair thou ow'st;",
-            "Nor shall death brag thou wander'st in his shade,",
-            "When in eternal lines to time thou grow'st:",
-            "   So long as men can breathe or eyes can see,",
-            "   So long lives this, and this gives life to thee.",
+            "Who has seen the wind?",
+            "Neither I nor you.",
+            "But when the leaves hang trembling,",
+            "The wind is passing through.",
+            "Who has seen the wind?",
+            "Neither you nor I.",
+            "But when the trees bow down their heads,",
+            "The wind is passing by."
     };
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index c60abe8..f69d7c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -31,6 +31,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -113,7 +114,7 @@
     @Test
     public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() {
         // Create a display which only contains 2 stacks.
-        final DisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
+        final DisplayContent display = addNewDisplayContentAt(POSITION_TOP);
         final Task stack1 = createFullscreenStackWithSimpleActivityAt(display);
         final Task stack2 = createFullscreenStackWithSimpleActivityAt(display);
 
@@ -228,7 +229,8 @@
         final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
                 .setStack(alwaysOnTopStack).build();
         alwaysOnTopStack.setAlwaysOnTop(true);
-        taskDisplayArea.positionStackAtTop(alwaysOnTopStack, false /* includingParents */);
+        taskDisplayArea.positionChildAt(POSITION_TOP, alwaysOnTopStack,
+                false /* includingParents */);
         assertTrue(alwaysOnTopStack.isAlwaysOnTop());
         // Ensure always on top state is synced to the children of the stack.
         assertTrue(alwaysOnTopStack.getTopNonFinishingActivity().isAlwaysOnTop());
@@ -242,7 +244,8 @@
         final Task anotherAlwaysOnTopStack = taskDisplayArea.createStack(
                 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         anotherAlwaysOnTopStack.setAlwaysOnTop(true);
-        taskDisplayArea.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
+        taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack,
+                false /* includingParents */);
         assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
         int topPosition = taskDisplayArea.getStackCount() - 1;
         // Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
@@ -258,7 +261,8 @@
         assertEquals(nonAlwaysOnTopStack, taskDisplayArea.getStackAt(topPosition - 3));
 
         anotherAlwaysOnTopStack.setAlwaysOnTop(false);
-        taskDisplayArea.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
+        taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack,
+                false /* includingParents */);
         assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
         // Ensure, when always on top is turned off for a stack, the stack is put just below all
         // other always on top stacks.
@@ -325,7 +329,7 @@
 
         // Reordering stacks while removing stacks.
         doAnswer(invocation -> {
-            taskDisplayArea.positionStackAtTop(stack3, false);
+            taskDisplayArea.positionChildAt(POSITION_TOP, stack3, false /*includingParents*/);
             return true;
         }).when(mSupervisor).removeTask(eq(task4), anyBoolean(), anyBoolean(), any());
 
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 5153af2..775df74 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -48,6 +48,7 @@
 import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE;
 import static com.android.server.wm.Task.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
 import static com.android.server.wm.TaskDisplayArea.getStackAbove;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -210,7 +211,7 @@
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
         final Task secondaryTask = mDefaultTaskDisplayArea.createStack(
                 WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        mDefaultTaskDisplayArea.positionStackAtTop(organizer.mPrimary,
+        mDefaultTaskDisplayArea.positionChildAt(POSITION_TOP, organizer.mPrimary,
                 false /* includingParents */);
 
         // Move primary to back.
@@ -988,18 +989,14 @@
     @SuppressWarnings("TypeParameterUnusedInFormals")
     private Task createStackForShouldBeVisibleTest(
             TaskDisplayArea taskDisplayArea, int windowingMode, int activityType, boolean onTop) {
-        final Task stack;
+        final Task task;
         if (activityType == ACTIVITY_TYPE_HOME) {
             // Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
-            stack = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
-            if (onTop) {
-                mDefaultTaskDisplayArea.positionStackAtTop(stack,
-                        false /* includingParents */);
-            } else {
-                mDefaultTaskDisplayArea.positionStackAtBottom(stack);
-            }
+            task = mDefaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+            mDefaultTaskDisplayArea.positionChildAt(onTop ? POSITION_TOP : POSITION_BOTTOM, task,
+                    false /* includingParents */);
         } else {
-            stack = new StackBuilder(mRootWindowContainer)
+            task = new StackBuilder(mRootWindowContainer)
                     .setTaskDisplayArea(taskDisplayArea)
                     .setWindowingMode(windowingMode)
                     .setActivityType(activityType)
@@ -1007,7 +1004,7 @@
                     .setCreateActivity(true)
                     .build();
         }
-        return stack;
+        return task;
     }
 
     @Test
@@ -1256,7 +1253,8 @@
                     mDefaultTaskDisplayArea, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                     true /* onTop */);
             mDefaultTaskDisplayArea.registerStackOrderChangedListener(listener);
-            mDefaultTaskDisplayArea.positionStackAtBottom(fullscreenStack1);
+            mDefaultTaskDisplayArea.positionChildAt(POSITION_BOTTOM, fullscreenStack1,
+                    false /*includingParents*/);
         } finally {
             mDefaultTaskDisplayArea.unregisterStackOrderChangedListener(listener);
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 3772e25..d07000f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -74,6 +74,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.graphics.Rect;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.RemoteException;
@@ -665,7 +666,9 @@
         mService.mStackSupervisor.setRecentTasks(recentTasks);
         doReturn(callerIsRecents).when(recentTasks).isCallerRecents(callingUid);
         // caller is temp allowed
-        callerApp.setAllowBackgroundActivityStarts(callerIsTempAllowed);
+        if (callerIsTempAllowed) {
+            callerApp.addAllowBackgroundActivityStartsToken(new Binder(), null);
+        }
         // caller is instrumenting with background activity starts privileges
         callerApp.setInstrumenting(callerIsInstrumentingWithBackgroundActivityStartPrivileges,
                 callerIsInstrumentingWithBackgroundActivityStartPrivileges);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index e3cfe11..e5d1e46 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -33,6 +33,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
 import static com.android.server.wm.Task.ActivityState.PAUSED;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -118,7 +119,8 @@
         TaskDisplayArea defaultTaskDisplayArea = mRootWindowContainer.getDefaultTaskDisplayArea();
         final Task homeStack =
                 defaultTaskDisplayArea.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
-        defaultTaskDisplayArea.positionStackAtTop(homeStack, false /* includingParents */);
+        defaultTaskDisplayArea.positionChildAt(POSITION_TOP, homeStack,
+                false /* includingParents */);
         ActivityRecord topRunningHomeActivity = homeStack.topRunningActivity();
         if (topRunningHomeActivity == null) {
             topRunningHomeActivity = new ActivityBuilder(mService)
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 3a5d333..74be2c9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -40,6 +40,7 @@
 import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
 import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
 import static com.android.server.wm.Task.ActivityState.STOPPED;
+import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -459,7 +460,7 @@
                 ACTIVITY_TYPE_STANDARD, false /* onTop */));
         final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
         final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
-        taskDisplayArea.positionStackAtBottom(targetStack);
+        taskDisplayArea.positionChildAt(POSITION_BOTTOM, targetStack, false /*includingParents*/);
 
         // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
         // is the current top focused stack.
@@ -560,7 +561,7 @@
         final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
         final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
         activity.setState(ActivityState.RESUMED, "test");
-        taskDisplayArea.positionStackAtBottom(targetStack);
+        taskDisplayArea.positionChildAt(POSITION_BOTTOM, targetStack, false /*includingParents*/);
 
         // Assume the stack is at the topmost position
         assertFalse(targetStack.isTopStackInDisplayArea());
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 bd52e9a..800a5d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -53,7 +53,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
-import android.app.IRequestFinishCallback;
 import android.app.PictureInPictureParams;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
@@ -976,8 +975,7 @@
         assertTrue(stack.isOrganized());
 
         // Verify a back pressed does not call the organizer
-        mWm.mAtmService.onBackPressedOnTaskRoot(activity.token,
-                new IRequestFinishCallback.Default());
+        mWm.mAtmService.onBackPressedOnTaskRoot(activity.token);
         verify(organizer, never()).onBackPressedOnTaskRoot(any());
 
         // Enable intercepting back
@@ -985,8 +983,7 @@
                 true);
 
         // Verify now that the back press does call the organizer
-        mWm.mAtmService.onBackPressedOnTaskRoot(activity.token,
-                new IRequestFinishCallback.Default());
+        mWm.mAtmService.onBackPressedOnTaskRoot(activity.token);
         verify(organizer, times(1)).onBackPressedOnTaskRoot(any());
     }
 
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index 7d3fde6..48794cb 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -306,11 +306,12 @@
     /** Check if location permissions have been granted */
     public static LocationPermissionResult checkLocationPermission(
             Context context, LocationPermissionQuery query) {
-        // Always allow the phone process and system server to access location. This avoid
-        // breaking legacy code that rely on public-facing APIs to access cell location, and
-        // it doesn't create an info leak risk because the cell location is stored in the phone
+        // Always allow the phone process, system server, and network stack to access location.
+        // This avoid breaking legacy code that rely on public-facing APIs to access cell location,
+        // and it doesn't create an info leak risk because the cell location is stored in the phone
         // process anyway, and the system server already has location access.
         if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID
+                || query.callingUid == Process.NETWORK_STACK_UID
                 || query.callingUid == Process.ROOT_UID) {
             return LocationPermissionResult.ALLOWED;
         }
diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java
index 8660e38..3dbbf41 100644
--- a/telephony/java/android/telephony/PreciseDataConnectionState.java
+++ b/telephony/java/android/telephony/PreciseDataConnectionState.java
@@ -53,14 +53,13 @@
  *
  */
 public final class PreciseDataConnectionState implements Parcelable {
-
-    private @DataState int mState = TelephonyManager.DATA_UNKNOWN;
-    private @NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
-    private @DataFailureCause int mFailCause = DataFailCause.NONE;
-    private @ApnType int mApnTypes = ApnSetting.TYPE_NONE;
-    private String mApn = "";
-    private LinkProperties mLinkProperties = null;
-    private ApnSetting mApnSetting = null;
+    private final @DataState int mState;
+    private final @NetworkType int mNetworkType;
+    private final @DataFailureCause int mFailCause;
+    private final @ApnType int mApnTypes;
+    private final String mApn;
+    private final LinkProperties mLinkProperties;
+    private final ApnSetting mApnSetting;
 
     /**
      * Constructor
@@ -84,20 +83,21 @@
     /**
      * Constructor of PreciseDataConnectionState
      *
-     * @param state the state of the data connection
-     * @param networkType the access network that is/would carry this data connection
-     * @param apnTypes the APN types that this data connection carries
-     * @param apn the APN of this data connection
-     * @param linkProperties if the data connection is connected, the properties of the connection
-     * @param failCause in case a procedure related to this data connection fails, a non-zero error
+     * @param state The state of the data connection
+     * @param networkType The access network that is/would carry this data connection
+     * @param apnTypes The APN types that this data connection carries
+     * @param apn The APN of this data connection
+     * @param linkProperties If the data connection is connected, the properties of the connection
+     * @param failCause In case a procedure related to this data connection fails, a non-zero error
      *        code indicating the cause of the failure.
-     * @param apnSetting if there is a valid APN for this Data Connection, then the APN Settings;
+     * @param apnSetting If there is a valid APN for this Data Connection, then the APN Settings;
      *        if there is no valid APN setting for the specific type, then this will be null
      * @hide
      */
-    public PreciseDataConnectionState(@DataState int state,
+    private PreciseDataConnectionState(@DataState int state,
                                       @NetworkType int networkType,
-                                      @ApnType int apnTypes, @NonNull String apn,
+                                      @ApnType int apnTypes,
+                                      @NonNull String apn,
                                       @Nullable LinkProperties linkProperties,
                                       @DataFailureCause int failCause,
                                       @Nullable ApnSetting apnSetting) {
@@ -111,14 +111,6 @@
     }
 
     /**
-     * Empty Constructor
-     *
-     * @hide
-     */
-    public PreciseDataConnectionState() {
-    }
-
-    /**
      * Construct a PreciseDataConnectionState object from the given parcel.
      *
      * @hide
@@ -167,9 +159,9 @@
     }
 
     /**
-     * Returns the network type associated with this data connection.
+     * Get the network type associated with this data connection.
      *
-     * Return the current/latest (radio) bearer technology that carries this data connection.
+     * @return The current/latest (radio) bearer technology that carries this data connection.
      * For a variety of reasons, the network type can change during the life of the data
      * connection, and this information is not reliable unless the physical link is currently
      * active; (there is currently no mechanism to know whether the physical link is active at
@@ -274,24 +266,23 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mState, mNetworkType, mApnTypes, mApn, mLinkProperties,
-                mFailCause, mApnSetting);
+        return Objects.hash(mState, mNetworkType, mFailCause, mApnTypes, mApn, mLinkProperties,
+                mApnSetting);
     }
 
+
     @Override
-    public boolean equals(@Nullable Object obj) {
-
-        if (!(obj instanceof PreciseDataConnectionState)) {
-            return false;
-        }
-
-        PreciseDataConnectionState other = (PreciseDataConnectionState) obj;
-        return Objects.equals(mApn, other.mApn) && mApnTypes == other.mApnTypes
-                && mFailCause == other.mFailCause
-                && Objects.equals(mLinkProperties, other.mLinkProperties)
-                && mNetworkType == other.mNetworkType
-                && mState == other.mState
-                && Objects.equals(mApnSetting, other.mApnSetting);
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PreciseDataConnectionState that = (PreciseDataConnectionState) o;
+        return mState == that.mState
+                && mNetworkType == that.mNetworkType
+                && mFailCause == that.mFailCause
+                && mApnTypes == that.mApnTypes
+                && Objects.equals(mApn, that.mApn)
+                && Objects.equals(mLinkProperties, that.mLinkProperties)
+                && Objects.equals(mApnSetting, that.mApnSetting);
     }
 
     @NonNull
@@ -309,4 +300,123 @@
 
         return sb.toString();
     }
+
+    /**
+     * {@link PreciseDataConnectionState} builder
+     *
+     * @hide
+     */
+    public static final class Builder {
+        /** The state of the data connection */
+        private @DataState int mState = TelephonyManager.DATA_UNKNOWN;
+
+        /** The network type associated with this data connection */
+        private @NetworkType int mNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+
+        /** The APN types that this data connection carries */
+        private @ApnType int mApnTypes = ApnSetting.TYPE_NONE;
+
+        /** The APN of this data connection */
+        private @NonNull String mApn = "";
+
+        /** If the data connection is connected, the properties of the connection */
+        private @Nullable LinkProperties mLinkProperties = null;
+
+        /**
+         * In case a procedure related to this data connection fails, a non-zero error code
+         * indicating the cause of the failure.
+         */
+        private @DataFailureCause int mFailCause = DataFailCause.NONE;
+
+        /** The APN Setting for this data connection */
+        private @Nullable ApnSetting mApnSetting = null;
+
+        /**
+         * Set the state of the data connection.
+         *
+         * @param state The state of the data connection
+         * @return The builder
+         */
+        public Builder setState(@DataState int state) {
+            mState = state;
+            return this;
+        }
+
+        /**
+         * Set the network type associated with this data connection.
+         *
+         * @param networkType The network type
+         * @return The builder
+         */
+        public Builder setNetworkType(@NetworkType int networkType) {
+            mNetworkType = networkType;
+            return this;
+        }
+
+        /**
+         * Set the APN types that this data connection carries
+         *
+         * @param apnTypes The APN types
+         * @return The builder
+         */
+        public Builder setApnTypes(@ApnType int apnTypes) {
+            mApnTypes = apnTypes;
+            return this;
+        }
+
+        /**
+         * Set the APN of this data connection
+         *
+         * @param apn The APN of this data connection
+         * @return The builder
+         */
+        public Builder setApn(@NonNull String apn) {
+            mApn = apn;
+            return this;
+        }
+
+        /**
+         * Set the link properties of the connection.
+         *
+         * @param linkProperties Link properties
+         * @return The builder
+         */
+        public Builder setLinkProperties(@NonNull LinkProperties linkProperties) {
+            mLinkProperties = linkProperties;
+            return this;
+        }
+
+        /**
+         * Set the fail cause of the data connection.
+         *
+         * @param failCause In case a procedure related to this data connection fails, a non-zero
+         * error code indicating the cause of the failure.
+         * @return The builder
+         */
+        public Builder setFailCause(@DataFailureCause int failCause) {
+            mFailCause = failCause;
+            return this;
+        }
+
+        /**
+         * Set the APN Setting for this data connection.
+         *
+         * @param apnSetting APN setting
+         * @return This builder
+         */
+        public Builder setApnSetting(@NonNull ApnSetting apnSetting) {
+            mApnSetting = apnSetting;
+            return this;
+        }
+
+        /**
+         * Build the {@link PreciseDataConnectionState} instance.
+         *
+         * @return The {@link PreciseDataConnectionState} instance
+         */
+        public PreciseDataConnectionState build() {
+            return new PreciseDataConnectionState(mState, mNetworkType, mApnTypes, mApn,
+                    mLinkProperties, mFailCause, mApnSetting);
+        }
+    }
 }
diff --git a/tests/Internal/src/android/app/WallpaperColorsTest.java b/tests/Internal/src/android/app/WallpaperColorsTest.java
index e9bac71..45d3dade 100644
--- a/tests/Internal/src/android/app/WallpaperColorsTest.java
+++ b/tests/Internal/src/android/app/WallpaperColorsTest.java
@@ -47,7 +47,7 @@
     }
 
     /**
-     * Sanity check to guarantee that white supports dark text and black doesn't
+     * Check that white supports dark text and black doesn't
      */
     @Test
     public void colorHintsTest() {